Merge "Add owners for window coretest"
diff --git a/Android.bp b/Android.bp
index afd8bfd..856e11ec 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,7 +112,6 @@
":gsiservice_aidl",
":incidentcompanion_aidl",
":installd_aidl",
- ":keystore_aidl",
":libaudioclient_aidl",
":libbinder_aidl",
":libbluetooth-binder-aidl",
@@ -540,7 +539,6 @@
"core/java/com/android/internal/util/RingBufferIndices.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
"core/java/com/android/internal/util/TokenBucket.java",
],
@@ -566,7 +564,6 @@
"core/java/com/android/internal/util/MessageUtils.java",
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
- "core/java/com/android/internal/util/TrafficStatsConstants.java",
"core/java/com/android/internal/util/WakeupMessage.java",
],
}
@@ -788,6 +785,14 @@
path: "core/java",
}
+filegroup {
+ name: "activity_manager_procstate_aidl",
+ srcs: [
+ // internal only
+ ],
+ path: "core/java",
+}
+
aidl_interface {
name: "libincremental_aidl",
unstable: true,
diff --git a/boot/Android.bp b/boot/Android.bp
index 4f7c44e..71edea2 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -23,7 +23,24 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-boot_image {
- name: "framework-boot-image",
- image_name: "boot",
+// This module provides access to information Soong has related to the
+// whole platform bootclasspath. Currently, that information is provided solely
+// through configuration but additional information will be added here.
+//
+// This will provide support for the following:
+// * Hidden API processing for those parts of the bootclasspath that are not
+// part of an APEX.
+// * Compatibility checking to ensure that the hidden API bits encoded into the
+// dex files by the modularized hidden API processing is compatible with the
+// runtimes of earlier releases which expect the bits to have been computed
+// over the entirety of the bootclasspath in one go not separately.
+// * Dexpreopting apps and other libraries not on the platform bootclasspath.
+// * Generating and installing the appropriate files to the device which will
+// allow it to generate the bootclasspath related environment variables
+// dynamically.
+//
+// This module needs to be present in the build for the above processing to be
+// done correctly.
+platform_bootclasspath {
+ name: "platform-bootclasspath",
}
diff --git a/config/hiddenapi-unsupported.txt b/config/hiddenapi-unsupported.txt
index 8a377ac..48a9782 100644
--- a/config/hiddenapi-unsupported.txt
+++ b/config/hiddenapi-unsupported.txt
@@ -209,7 +209,6 @@
Landroid/os/storage/IStorageManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/storage/IStorageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/storage/IStorageManager;
Landroid/security/IKeyChainService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeyChainService;
-Landroid/security/keystore/IKeystoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/keystore/IKeystoreService;
Landroid/service/dreams/IDreamManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/dreams/IDreamManager;
Landroid/service/notification/INotificationListener$Stub;-><init>()V
Landroid/service/persistentdata/IPersistentDataBlockService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/persistentdata/IPersistentDataBlockService;
diff --git a/core/api/current.txt b/core/api/current.txt
index f00f0ae..b944ac9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -25703,12 +25703,13 @@
public final class VcnGatewayConnectionConfig {
method @NonNull public int[] getExposedCapabilities();
+ method @NonNull public String getGatewayConnectionName();
method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
method @NonNull public long[] getRetryInterval();
}
public static final class VcnGatewayConnectionConfig.Builder {
- ctor public VcnGatewayConnectionConfig.Builder(@NonNull android.net.vcn.VcnControlPlaneConfig);
+ ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.vcn.VcnControlPlaneConfig);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
@@ -25732,7 +25733,7 @@
public abstract static class VcnManager.VcnStatusCallback {
ctor public VcnManager.VcnStatusCallback();
- method public abstract void onGatewayConnectionError(@NonNull int[], int, @Nullable Throwable);
+ method public abstract void onGatewayConnectionError(@NonNull String, int, @Nullable Throwable);
method public abstract void onStatusChanged(int);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 3f11b72..e6624cd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -11988,7 +11988,7 @@
method @NonNull public byte[] getEncodedMessage();
method @NonNull public String getHeaderSection();
method @NonNull public String getStartLine();
- method @Nullable public String getViaBranchParameter();
+ method @NonNull public String getViaBranchParameter();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 171c6a2..f50aa99 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -78,5 +78,4 @@
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
boolean isUidRestrictedOnMeteredNetworks(int uid);
- boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index b037261..1eef7d9 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -198,15 +198,11 @@
final int oemManaged = getOemBitfield(snapshot.networkCapabilities);
if (legacyType == TYPE_WIFI) {
- if (snapshot.networkCapabilities.getSsid() != null) {
- networkId = snapshot.networkCapabilities.getSsid();
- if (networkId == null) {
- // TODO: Figure out if this code path never runs. If so, remove them.
- final WifiManager wifi = (WifiManager) context.getSystemService(
- Context.WIFI_SERVICE);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
- }
+ networkId = snapshot.networkCapabilities.getSsid();
+ if (networkId == null) {
+ final WifiManager wifi = context.getSystemService(WifiManager.class);
+ final WifiInfo info = wifi.getConnectionInfo();
+ networkId = info != null ? info.getSSID() : null;
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 68606ec..da3febd 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -564,31 +564,6 @@
}
/**
- * Figure out if networking is blocked for a given set of conditions.
- *
- * This is used by ConnectivityService via passing stale copies of conditions, so it must not
- * take any locks.
- *
- * @param uid The target uid.
- * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
- * @param isNetworkMetered True if the network is metered.
- * @param isBackgroundRestricted True if data saver is enabled.
- *
- * @return true if networking is blocked for the UID under the specified conditions.
- *
- * @hide
- */
- public boolean checkUidNetworkingBlocked(int uid, int uidRules,
- boolean isNetworkMetered, boolean isBackgroundRestricted) {
- try {
- return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Check that the given uid is restricted from doing networking on metered networks.
*
* @param uid The target uid.
diff --git a/core/java/android/net/vcn/IVcnStatusCallback.aidl b/core/java/android/net/vcn/IVcnStatusCallback.aidl
index 236ae8b..11bc443 100644
--- a/core/java/android/net/vcn/IVcnStatusCallback.aidl
+++ b/core/java/android/net/vcn/IVcnStatusCallback.aidl
@@ -20,7 +20,7 @@
oneway interface IVcnStatusCallback {
void onVcnStatusChanged(int statusCode);
void onGatewayConnectionError(
- in int[] gatewayNetworkCapabilities,
+ in String gatewayConnectionName,
int errorCode,
in String exceptionClass,
in String exceptionMessage);
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index 52cc218..d41c0b4 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -183,12 +183,25 @@
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
+ * @throws IllegalArgumentException if a VcnGatewayConnectionConfig has already been set for
+ * this {@link VcnConfig} with the same GatewayConnection name (as returned via {@link
+ * VcnGatewayConnectionConfig#getGatewayConnectionName()}).
*/
@NonNull
public Builder addGatewayConnectionConfig(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
Objects.requireNonNull(gatewayConnectionConfig, "gatewayConnectionConfig was null");
+ for (final VcnGatewayConnectionConfig vcnGatewayConnectionConfig :
+ mGatewayConnectionConfigs) {
+ if (vcnGatewayConnectionConfig
+ .getGatewayConnectionName()
+ .equals(gatewayConnectionConfig.getGatewayConnectionName())) {
+ throw new IllegalArgumentException(
+ "GatewayConnection for specified name already exists");
+ }
+ }
+
mGatewayConnectionConfigs.add(gatewayConnectionConfig);
return this;
}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d4e8e2d..75db3820 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -148,6 +148,8 @@
TimeUnit.MINUTES.toMillis(5),
TimeUnit.MINUTES.toMillis(15)
};
+ private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName";
+ @NonNull private final String mGatewayConnectionName;
private static final String CTRL_PLANE_CONFIG_KEY = "mCtrlPlaneConfig";
@NonNull private VcnControlPlaneConfig mCtrlPlaneConfig;
@@ -166,11 +168,13 @@
/** Builds a VcnGatewayConnectionConfig with the specified parameters. */
private VcnGatewayConnectionConfig(
+ @NonNull String gatewayConnectionName,
@NonNull VcnControlPlaneConfig ctrlPlaneConfig,
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
+ mGatewayConnectionName = gatewayConnectionName;
mCtrlPlaneConfig = ctrlPlaneConfig;
mExposedCapabilities = new TreeSet(exposedCapabilities);
mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
@@ -192,6 +196,7 @@
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
+ mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY);
mCtrlPlaneConfig = VcnControlPlaneConfig.fromPersistableBundle(ctrlPlaneConfigBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
@@ -204,6 +209,7 @@
}
private void validate() {
+ Objects.requireNonNull(mGatewayConnectionName, "gatewayConnectionName was null");
Objects.requireNonNull(mCtrlPlaneConfig, "control plane config was null");
Preconditions.checkArgument(
@@ -242,6 +248,20 @@
}
/**
+ * Returns the configured Gateway Connection name.
+ *
+ * <p>This name is used by the configuring apps to distinguish between
+ * VcnGatewayConnectionConfigs configured on a single {@link VcnConfig}. This will be used as
+ * the identifier in VcnStatusCallback invocations.
+ *
+ * @see VcnManager.VcnStatusCallback#onGatewayConnectionError
+ */
+ @NonNull
+ public String getGatewayConnectionName() {
+ return mGatewayConnectionName;
+ }
+
+ /**
* Returns control plane configuration.
*
* @hide
@@ -364,6 +384,7 @@
new ArrayList<>(mUnderlyingCapabilities),
PersistableBundleUtils.INTEGER_SERIALIZER);
+ result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(CTRL_PLANE_CONFIG_KEY, ctrlPlaneConfigBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
result.putPersistableBundle(UNDERLYING_CAPABILITIES_KEY, underlyingCapsBundle);
@@ -376,6 +397,7 @@
@Override
public int hashCode() {
return Objects.hash(
+ mGatewayConnectionName,
mExposedCapabilities,
mUnderlyingCapabilities,
Arrays.hashCode(mRetryIntervalsMs),
@@ -389,7 +411,8 @@
}
final VcnGatewayConnectionConfig rhs = (VcnGatewayConnectionConfig) other;
- return mExposedCapabilities.equals(rhs.mExposedCapabilities)
+ return mGatewayConnectionName.equals(rhs.mGatewayConnectionName)
+ && mExposedCapabilities.equals(rhs.mExposedCapabilities)
&& mUnderlyingCapabilities.equals(rhs.mUnderlyingCapabilities)
&& Arrays.equals(mRetryIntervalsMs, rhs.mRetryIntervalsMs)
&& mMaxMtu == rhs.mMaxMtu;
@@ -399,6 +422,7 @@
* This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
*/
public static final class Builder {
+ @NonNull private final String mGatewayConnectionName;
@NonNull private final VcnControlPlaneConfig mCtrlPlaneConfig;
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@@ -412,12 +436,22 @@
/**
* Construct a Builder object.
*
+ * @param gatewayConnectionName the String GatewayConnection name for this
+ * VcnGatewayConnectionConfig. Each VcnGatewayConnectionConfig within a {@link
+ * VcnConfig} must be given a unique name. This name is used by the caller to
+ * distinguish between VcnGatewayConnectionConfigs configured on a single {@link
+ * VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
* @param ctrlPlaneConfig the control plane configuration
* @see VcnControlPlaneConfig
+ * @see VcnManager.VcnStatusCallback#onGatewayConnectionError
*/
- public Builder(@NonNull VcnControlPlaneConfig ctrlPlaneConfig) {
+ public Builder(
+ @NonNull String gatewayConnectionName,
+ @NonNull VcnControlPlaneConfig ctrlPlaneConfig) {
+ Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
Objects.requireNonNull(ctrlPlaneConfig, "ctrlPlaneConfig was null");
+ mGatewayConnectionName = gatewayConnectionName;
mCtrlPlaneConfig = ctrlPlaneConfig;
}
@@ -562,6 +596,7 @@
@NonNull
public VcnGatewayConnectionConfig build() {
return new VcnGatewayConnectionConfig(
+ mGatewayConnectionName,
mCtrlPlaneConfig,
mExposedCapabilities,
mUnderlyingCapabilities,
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index abd41da..344b20c 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -437,7 +437,7 @@
* Invoked when status of the VCN for this callback's subscription group changes.
*
* @param statusCode the code for the status change encountered by this {@link
- * VcnStatusCallback}'s subscription group.
+ * VcnStatusCallback}'s subscription group. This value will be one of VCN_STATUS_CODE_*.
*/
public abstract void onStatusChanged(@VcnStatusCode int statusCode);
@@ -445,18 +445,17 @@
* Invoked when a VCN Gateway Connection corresponding to this callback's subscription group
* encounters an error.
*
- * @param networkCapabilities an array of NetworkCapabilities.NET_CAPABILITY_* capabilities
- * for the Gateway Connection that encountered the error, for identification purposes.
- * These will be a sorted list with no duplicates and will match {@link
- * VcnGatewayConnectionConfig#getExposedCapabilities()} for one of the {@link
- * VcnGatewayConnectionConfig}s set in the {@link VcnConfig} for this subscription
- * group.
- * @param errorCode the code to indicate the error that occurred
+ * @param gatewayConnectionName the String GatewayConnection name for the GatewayConnection
+ * encountering an error. This will match the name for exactly one {@link
+ * VcnGatewayConnectionConfig} for the {@link VcnConfig} configured for this callback's
+ * subscription group
+ * @param errorCode the code to indicate the error that occurred. This value will be one of
+ * VCN_ERROR_CODE_*.
* @param detail Throwable to provide additional information about the error, or {@code
* null} if none
*/
public abstract void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable Throwable detail);
}
@@ -586,7 +585,7 @@
// TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling'
@Override
public void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage) {
@@ -597,7 +596,7 @@
mExecutor.execute(
() ->
mCallback.onGatewayConnectionError(
- networkCapabilities, errorCode, cause)));
+ gatewayConnectionName, errorCode, cause)));
}
private static Throwable createThrowableByClassName(
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index 2329037..d8c44ad 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.text.TextUtils;
import android.util.Log;
@@ -106,32 +105,6 @@
private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
ConfirmationCallback callback) {
switch (responseCode) {
- case KeyStore.CONFIRMATIONUI_OK:
- callback.onConfirmed(dataThatWasConfirmed);
- break;
-
- case KeyStore.CONFIRMATIONUI_CANCELED:
- callback.onDismissed();
- break;
-
- case KeyStore.CONFIRMATIONUI_ABORTED:
- callback.onCanceled();
- break;
-
- case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
- callback.onError(new Exception("System error returned by ConfirmationUI."));
- break;
-
- default:
- callback.onError(new Exception("Unexpected responseCode=" + responseCode
- + " from onConfirmtionPromptCompleted() callback."));
- break;
- }
- }
-
- private void doCallback2(int responseCode, byte[] dataThatWasConfirmed,
- ConfirmationCallback callback) {
- switch (responseCode) {
case AndroidProtectedConfirmation.ERROR_OK:
callback.onConfirmed(dataThatWasConfirmed);
break;
@@ -155,31 +128,6 @@
}
}
- private final android.os.IBinder mCallbackBinder =
- new android.security.IConfirmationPromptCallback.Stub() {
- @Override
- public void onConfirmationPromptCompleted(
- int responseCode, final byte[] dataThatWasConfirmed)
- throws android.os.RemoteException {
- if (mCallback != null) {
- ConfirmationCallback callback = mCallback;
- Executor executor = mExecutor;
- mCallback = null;
- mExecutor = null;
- if (executor == null) {
- doCallback(responseCode, dataThatWasConfirmed, callback);
- } else {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- doCallback(responseCode, dataThatWasConfirmed, callback);
- }
- });
- }
- }
- }
- };
-
private final android.security.apc.IConfirmationCallback mConfirmationCallback =
new android.security.apc.IConfirmationCallback.Stub() {
@Override
@@ -191,11 +139,11 @@
mCallback = null;
mExecutor = null;
if (executor == null) {
- doCallback2(result, dataThatWasConfirmed, callback);
+ doCallback(result, dataThatWasConfirmed, callback);
} else {
executor.execute(new Runnable() {
@Override public void run() {
- doCallback2(result, dataThatWasConfirmed, callback);
+ doCallback(result, dataThatWasConfirmed, callback);
}
});
}
@@ -266,29 +214,7 @@
mExtraData = extraData;
}
- private static final int UI_OPTION_ACCESSIBILITY_INVERTED_FLAG = 1 << 0;
- private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1;
-
private int getUiOptionsAsFlags() {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- return getUiOptionsAsFlags2();
- }
- int uiOptionsAsFlags = 0;
- ContentResolver contentResolver = mContext.getContentResolver();
- int inversionEnabled = Settings.Secure.getInt(contentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
- if (inversionEnabled == 1) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
- }
- float fontScale = Settings.System.getFloat(contentResolver,
- Settings.System.FONT_SCALE, (float) 1.0);
- if (fontScale > 1.0) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
- }
- return uiOptionsAsFlags;
- }
-
- private int getUiOptionsAsFlags2() {
int uiOptionsAsFlags = 0;
ContentResolver contentResolver = mContext.getContentResolver();
int inversionEnabled = Settings.Secure.getInt(contentResolver,
@@ -349,52 +275,26 @@
mExecutor = executor;
String locale = Locale.getDefault().toLanguageTag();
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- int uiOptionsAsFlags = getUiOptionsAsFlags2();
- int responseCode = getService().presentConfirmationPrompt(
- mConfirmationCallback, mPromptText.toString(), mExtraData, locale,
- uiOptionsAsFlags);
- switch (responseCode) {
- case AndroidProtectedConfirmation.ERROR_OK:
- return;
+ int uiOptionsAsFlags = getUiOptionsAsFlags();
+ int responseCode = getService().presentConfirmationPrompt(
+ mConfirmationCallback, mPromptText.toString(), mExtraData, locale,
+ uiOptionsAsFlags);
+ switch (responseCode) {
+ case AndroidProtectedConfirmation.ERROR_OK:
+ return;
- case AndroidProtectedConfirmation.ERROR_OPERATION_PENDING:
- throw new ConfirmationAlreadyPresentingException();
+ case AndroidProtectedConfirmation.ERROR_OPERATION_PENDING:
+ throw new ConfirmationAlreadyPresentingException();
- case AndroidProtectedConfirmation.ERROR_UNIMPLEMENTED:
- throw new ConfirmationNotAvailableException();
+ case AndroidProtectedConfirmation.ERROR_UNIMPLEMENTED:
+ throw new ConfirmationNotAvailableException();
- default:
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from presentConfirmationPrompt() call.");
- throw new IllegalArgumentException();
- }
- } else {
- int uiOptionsAsFlags = getUiOptionsAsFlags();
- int responseCode = mKeyStore.presentConfirmationPrompt(
- mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
- switch (responseCode) {
- case KeyStore.CONFIRMATIONUI_OK:
- return;
-
- case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
- throw new ConfirmationAlreadyPresentingException();
-
- case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
- throw new ConfirmationNotAvailableException();
-
- case KeyStore.CONFIRMATIONUI_UIERROR:
- throw new IllegalArgumentException();
-
- default:
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from presentConfirmationPrompt() call.");
- throw new IllegalArgumentException();
- }
+ default:
+ // Unexpected error code.
+ Log.w(TAG,
+ "Unexpected responseCode=" + responseCode
+ + " from presentConfirmationPrompt() call.");
+ throw new IllegalArgumentException();
}
}
@@ -408,33 +308,18 @@
* @throws IllegalStateException if no prompt is currently being presented.
*/
public void cancelPrompt() {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- int responseCode =
- getService().cancelConfirmationPrompt(mConfirmationCallback);
- if (responseCode == AndroidProtectedConfirmation.ERROR_OK) {
- return;
- } else if (responseCode == AndroidProtectedConfirmation.ERROR_OPERATION_PENDING) {
- throw new IllegalStateException();
- } else {
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from cancelConfirmationPrompt() call.");
- throw new IllegalStateException();
- }
+ int responseCode =
+ getService().cancelConfirmationPrompt(mConfirmationCallback);
+ if (responseCode == AndroidProtectedConfirmation.ERROR_OK) {
+ return;
+ } else if (responseCode == AndroidProtectedConfirmation.ERROR_OPERATION_PENDING) {
+ throw new IllegalStateException();
} else {
- int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
- if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
- return;
- } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
- throw new IllegalStateException();
- } else {
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from cancelConfirmationPrompt() call.");
- throw new IllegalStateException();
- }
+ // Unexpected error code.
+ Log.w(TAG,
+ "Unexpected responseCode=" + responseCode
+ + " from cancelConfirmationPrompt() call.");
+ throw new IllegalStateException();
}
}
@@ -448,9 +333,6 @@
if (isAccessibilityServiceRunning(context)) {
return false;
}
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- return new AndroidProtectedConfirmation().isConfirmationPromptSupported();
- }
- return KeyStore.getInstance().isConfirmationPromptSupported();
+ return new AndroidProtectedConfirmation().isConfirmationPromptSupported();
}
}
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
new file mode 100644
index 0000000..e01db7a
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keymaster;
+
+parcelable KeymasterCertificateChain;
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 1dc7f71..17ad5f1 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -712,18 +712,10 @@
*/
@NonNull Key getKeyFromGrant(@NonNull String grantAlias)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) {
- return AndroidKeyStoreProvider
- .loadAndroidKeyStoreSecretKeyFromKeystore(
- KeyStore2.getInstance(),
- getGrantDescriptor(grantAlias));
- }
- // TODO(b/171305545): remove KeyStore1 logic.
- return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
- mKeyStore,
- grantAlias,
- KeyStore.UID_SELF);
-
+ return AndroidKeyStoreProvider
+ .loadAndroidKeyStoreSecretKeyFromKeystore(
+ KeyStore2.getInstance(),
+ getGrantDescriptor(grantAlias));
}
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index beef982..0b78a2b 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -32,7 +32,7 @@
interface IBatteryStats {
// These first methods are also called by native code, so must
- // be kept in sync with frameworks/native/libs/binder/include/binder/IBatteryStats.h
+ // be kept in sync with frameworks/native/libs/binder/include_batterystats/batterystats/IBatteryStats.h
void noteStartSensor(int uid, int sensor);
void noteStopSensor(int uid, int sensor);
void noteStartVideo(int uid);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 5fea76a..a541089 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,6 +41,7 @@
import android.os.ZygoteProcess;
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
+import android.security.keystore2.AndroidKeyStoreProvider;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -222,10 +223,8 @@
long startTime = SystemClock.uptimeMillis();
Trace.traceBegin(
Trace.TRACE_TAG_DALVIK, "Starting installation of AndroidKeyStoreProvider");
- // AndroidKeyStoreProvider.install() manipulates the list of JCA providers to insert
- // preferred providers. Note this is not done via security.properties as the JCA providers
- // are not on the classpath in the case of, for example, raw dalvikvm runtimes.
- android.security.keystore2.AndroidKeyStoreProvider.install();
+
+ AndroidKeyStoreProvider.install();
Log.i(TAG, "Installed AndroidKeyStoreProvider in "
+ (SystemClock.uptimeMillis() - startTime) + "ms.");
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index f71b314..4d2266b 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -547,7 +547,7 @@
try {
ZygoteConnection connection = peers.get(pollIndex);
boolean multipleForksOK = !isUsapPoolEnabled()
- && ZygoteHooks.indefiniteThreadSuspensionOK();
+ && ZygoteHooks.isIndefiniteThreadSuspensionSafe();
final Runnable command =
connection.processCommand(this, multipleForksOK);
diff --git a/core/java/com/android/internal/util/TrafficStatsConstants.java b/core/java/com/android/internal/util/TrafficStatsConstants.java
index 413be48..131114c 100644
--- a/core/java/com/android/internal/util/TrafficStatsConstants.java
+++ b/core/java/com/android/internal/util/TrafficStatsConstants.java
@@ -21,24 +21,8 @@
* @hide
*/
public class TrafficStatsConstants {
- // These tags are used by the network stack to do traffic for its own purposes. Traffic
- // tagged with these will be counted toward the network stack and must stay inside the
- // range defined by
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_START} and
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_RANGE_END}.
- public static final int TAG_SYSTEM_DHCP = 0xFFFFFE01;
- public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFE02;
- public static final int TAG_SYSTEM_DHCP_SERVER = 0xFFFFFE03;
public static final int TAG_SYSTEM_NTP = 0xFFFFFF41;
public static final int TAG_SYSTEM_GPS = 0xFFFFFF44;
public static final int TAG_SYSTEM_PAC = 0xFFFFFF45;
-
- // These tags are used by the network stack to do traffic on behalf of apps. Traffic
- // tagged with these will be counted toward the app on behalf of which the network
- // stack is doing this traffic. These values must stay inside the range defined by
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_START} and
- // {@link android.net.TrafficStats#TAG_NETWORK_STACK_IMPERSONATION_RANGE_END}.
- public static final int TAG_SYSTEM_PROBE = 0xFFFFFF81;
- public static final int TAG_SYSTEM_DNS = 0xFFFFFF82;
}
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index c0e0a24..4a24b42 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -21,6 +21,7 @@
import android.util.Pools.SynchronizedPool;
import android.view.DisplayListCanvas;
import android.view.TextureLayer;
+import android.os.SystemProperties;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -41,7 +42,14 @@
private static final int POOL_LIMIT = 25;
/** @hide */
- public static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+ private static int getPanelFrameSize() {
+ final int DefaultSize = 100 * 1024 * 1024; // 100 MB;
+ return Math.max(SystemProperties.getInt("ro.hwui.max_texture_allocation_size", DefaultSize),
+ DefaultSize);
+ }
+
+ /** @hide */
+ public static final int MAX_BITMAP_SIZE = getPanelFrameSize();
private static final SynchronizedPool<RecordingCanvas> sPool =
new SynchronizedPool<>(POOL_LIMIT);
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 72cea0c..82639de 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -47,7 +47,6 @@
* @hide
*/
public static int onUserAdded(@NonNull int userId) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().onUserAdded(userId);
return 0;
@@ -68,7 +67,6 @@
* @hide
*/
public static int onUserRemoved(int userId) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().onUserRemoved(userId);
return 0;
@@ -91,7 +89,6 @@
* @hide
*/
public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().onUserPasswordChanged(userId, password);
return 0;
@@ -109,7 +106,6 @@
* be cleared.
*/
public static int clearNamespace(@Domain int domain, long namespace) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().clearNamespace(domain, namespace);
return 0;
@@ -144,7 +140,6 @@
* Informs Keystore 2.0 that an off body event was detected.
*/
public static void onDeviceOffBody() {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return;
try {
getService().onDeviceOffBody();
} catch (Exception e) {
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index 50a9082..bd72d45 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -48,7 +48,6 @@
* @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
*/
public static int addAuthToken(@NonNull HardwareAuthToken authToken) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
getService().addAuthToken(authToken);
return 0;
@@ -80,7 +79,6 @@
*/
public static int onLockScreenEvent(@NonNull boolean locked, @NonNull int userId,
@Nullable byte[] syntheticPassword) {
- if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
try {
if (locked) {
getService().onLockScreenEvent(LockScreenEvent.LOCK, userId, null);
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index ae9f8664..28c601b 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -208,78 +208,4 @@
pr.close();
}
}
-
- /**
- * Delete all types (private key, user certificate, CA certificate) for a
- * particular {@code alias}. All three can exist for any given alias.
- * Returns {@code true} if the alias no longer contains any types.
- */
- public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias) {
- return deleteAllTypesForAlias(keystore, alias, KeyStore.UID_SELF);
- }
-
- /**
- * Delete all types (private key, user certificate, CA certificate) for a
- * particular {@code alias}. All three can exist for any given alias.
- * Returns {@code true} if the alias no longer contains any types.
- */
- public static boolean deleteAllTypesForAlias(KeyStore keystore, String alias, int uid) {
- /*
- * Make sure every type is deleted. There can be all three types, so
- * don't use a conditional here.
- */
- return deleteUserKeyTypeForAlias(keystore, alias, uid)
- & deleteCertificateTypesForAlias(keystore, alias, uid);
- }
-
- /**
- * Delete certificate types (user certificate, CA certificate) for a
- * particular {@code alias}. Both can exist for any given alias.
- * Returns {@code true} if the alias no longer contains either type.
- */
- public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias) {
- return deleteCertificateTypesForAlias(keystore, alias, KeyStore.UID_SELF);
- }
-
- /**
- * Delete certificate types (user certificate, CA certificate) for a
- * particular {@code alias}. Both can exist for any given alias.
- * Returns {@code true} if the alias no longer contains either type.
- */
- public static boolean deleteCertificateTypesForAlias(KeyStore keystore, String alias, int uid) {
- /*
- * Make sure every certificate type is deleted. There can be two types,
- * so don't use a conditional here.
- */
- return keystore.delete(Credentials.USER_CERTIFICATE + alias, uid)
- & keystore.delete(Credentials.CA_CERTIFICATE + alias, uid);
- }
-
- /**
- * Delete user key for a particular {@code alias}.
- * Returns {@code true} if the entry no longer exists.
- */
- public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) {
- return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
- }
-
- /**
- * Delete user key for a particular {@code alias}.
- * Returns {@code true} if the entry no longer exists.
- */
- public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
- int ret = keystore.delete2(Credentials.USER_PRIVATE_KEY + alias, uid);
- if (ret == KeyStore.KEY_NOT_FOUND) {
- return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
- }
- return ret == KeyStore.NO_ERROR;
- }
-
- /**
- * Delete legacy prefixed entry for a particular {@code alias}
- * Returns {@code true} if the entry no longer exists.
- */
- public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) {
- return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
- }
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index d59ca98..fd0db93 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -37,7 +37,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Domain;
@@ -676,23 +675,13 @@
return null;
}
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- try {
- return android.security.keystore2.AndroidKeyStoreProvider
- .loadAndroidKeyStoreKeyPairFromKeystore(
- KeyStore2.getInstance(),
- getGrantDescriptor(keyId));
- } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
- throw new KeyChainException(e);
- }
- } else {
- try {
- return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
- } catch (RuntimeException | UnrecoverableKeyException
- | KeyPermanentlyInvalidatedException e) {
- throw new KeyChainException(e);
- }
+ try {
+ return android.security.keystore2.AndroidKeyStoreProvider
+ .loadAndroidKeyStoreKeyPairFromKeystore(
+ KeyStore2.getInstance(),
+ getGrantDescriptor(keyId));
+ } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
+ throw new KeyChainException(e);
}
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index b05149e..a954344 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,53 +16,11 @@
package android.security;
-import android.app.ActivityThread;
-import android.app.Application;
-import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.Context;
-import android.hardware.biometrics.BiometricManager;
-import android.os.Binder;
import android.os.Build;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
-import android.security.keymaster.ExportResult;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterBlob;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.IKeystoreService;
-import android.security.keystore.KeyExpiredException;
-import android.security.keystore.KeyNotYetValidException;
-import android.security.keystore.KeyPermanentlyInvalidatedException;
-import android.security.keystore.KeyProperties;
-import android.security.keystore.KeystoreResponse;
-import android.security.keystore.UserNotAuthenticatedException;
import android.security.maintenance.UserState;
import android.system.keystore2.Domain;
-import android.util.Log;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.internal.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-
-import sun.security.util.ObjectIdentifier;
-import sun.security.x509.AlgorithmId;
/**
* @hide This should not be made public in its present form because it
@@ -75,79 +33,10 @@
// ResponseCodes - see system/security/keystore/include/keystore/keystore.h
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int NO_ERROR = 1;
- public static final int LOCKED = 2;
- public static final int UNINITIALIZED = 3;
- public static final int SYSTEM_ERROR = 4;
- public static final int PROTOCOL_ERROR = 5;
- public static final int PERMISSION_DENIED = 6;
- public static final int KEY_NOT_FOUND = 7;
- public static final int VALUE_CORRUPTED = 8;
- public static final int UNDEFINED_ACTION = 9;
- public static final int WRONG_PASSWORD = 10;
- public static final int KEY_ALREADY_EXISTS = 16;
- public static final int CANNOT_ATTEST_IDS = -66;
- public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
-
- /**
- * Per operation authentication is needed before this operation is valid.
- * This is returned from {@link #begin} when begin succeeds but the operation uses
- * per-operation authentication and must authenticate before calling {@link #update} or
- * {@link #finish}.
- */
- public static final int OP_AUTH_NEEDED = 15;
-
- // Used when a user changes their pin, invalidating old auth bound keys.
- public static final int KEY_PERMANENTLY_INVALIDATED = 17;
// Used for UID field to indicate the calling UID.
public static final int UID_SELF = -1;
- // Flags for "put" "import" and "generate"
- public static final int FLAG_NONE = 0;
-
- /**
- * Indicates that this key (or key pair) must be encrypted at rest. This will protect the key
- * (or key pair) with the secure lock screen credential (e.g., password, PIN, or pattern).
- *
- * <p>Note that this requires that the secure lock screen (e.g., password, PIN, pattern) is set
- * up, otherwise key (or key pair) generation or import will fail. Moreover, this key (or key
- * pair) will be deleted when the secure lock screen is disabled or reset (e.g., by the user or
- * a Device Administrator). Finally, this key (or key pair) cannot be used until the user
- * unlocks the secure lock screen after boot.
- *
- * @see KeyguardManager#isDeviceSecure()
- */
- public static final int FLAG_ENCRYPTED = 1;
-
- /**
- * Select Software keymaster device, which as of this writing is the lowest security
- * level available on an android device. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided
- * A TEE based keymaster implementation is implied.
- *
- * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
- * For historical reasons this corresponds to the KEYSTORE_FLAG_FALLBACK flag.
- */
- public static final int FLAG_SOFTWARE = 1 << 1;
-
- /**
- * A private flag that's only available to system server to indicate that this key is part of
- * device encryption flow so it receives special treatment from keystore. For example this key
- * will not be super encrypted, and it will be stored separately under an unique UID instead
- * of the caller UID i.e. SYSTEM.
- *
- * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
- */
- public static final int FLAG_CRITICAL_TO_DEVICE_ENCRYPTION = 1 << 3;
-
- /**
- * Select Strongbox keymaster device, which as of this writing the the highest security level
- * available an android devices. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided
- * A TEE based keymaster implementation is implied.
- *
- * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
- */
- public static final int FLAG_STRONGBOX = 1 << 4;
-
// States
public enum State {
@UnsupportedAppUsage
@@ -157,853 +46,87 @@
UNINITIALIZED
};
- private int mError = NO_ERROR;
-
- private final IKeystoreService mBinder;
- private final Context mContext;
-
- private IBinder mToken;
-
- private KeyStore(IKeystoreService binder) {
- mBinder = binder;
- mContext = getApplicationContext();
- }
-
- @UnsupportedAppUsage
- public static Context getApplicationContext() {
- Application application = ActivityThread.currentApplication();
- if (application == null) {
- throw new IllegalStateException(
- "Failed to obtain application Context from ActivityThread");
- }
- return application;
- }
+ private static final KeyStore KEY_STORE = new KeyStore();
@UnsupportedAppUsage
public static KeyStore getInstance() {
- IKeystoreService keystore = IKeystoreService.Stub.asInterface(ServiceManager
- .getService("android.security.keystore"));
- return new KeyStore(keystore);
+ return KEY_STORE;
}
- private synchronized IBinder getToken() {
- if (mToken == null) {
- mToken = new Binder();
- }
- return mToken;
- }
-
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public State state(int userId) {
- final int ret;
- try {
- if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
- int userState = AndroidKeyStoreMaintenance.getState(userId);
- switch (userState) {
- case UserState.UNINITIALIZED:
- return KeyStore.State.UNINITIALIZED;
- case UserState.LSKF_UNLOCKED:
- return KeyStore.State.UNLOCKED;
- case UserState.LSKF_LOCKED:
- return KeyStore.State.LOCKED;
- default:
- throw new AssertionError(userState);
- }
- }
- ret = mBinder.getState(userId);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- throw new AssertionError(e);
- }
-
- switch (ret) {
- case NO_ERROR: return State.UNLOCKED;
- case LOCKED: return State.LOCKED;
- case UNINITIALIZED: return State.UNINITIALIZED;
- default: throw new AssertionError(mError);
+ int userState = AndroidKeyStoreMaintenance.getState(userId);
+ switch (userState) {
+ case UserState.UNINITIALIZED:
+ return KeyStore.State.UNINITIALIZED;
+ case UserState.LSKF_UNLOCKED:
+ return KeyStore.State.UNLOCKED;
+ case UserState.LSKF_LOCKED:
+ return KeyStore.State.LOCKED;
+ default:
+ throw new AssertionError(userState);
}
}
+ /** @hide */
@UnsupportedAppUsage
public State state() {
return state(UserHandle.myUserId());
}
- public boolean isUnlocked() {
- return state() == State.UNLOCKED;
- }
-
- public byte[] get(String key, int uid) {
- return get(key, uid, false);
- }
-
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public byte[] get(String key) {
- return get(key, UID_SELF);
+ return null;
}
- public byte[] get(String key, int uid, boolean suppressKeyNotFoundWarning) {
- try {
- key = key != null ? key : "";
- return mBinder.get(key, uid);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (android.os.ServiceSpecificException e) {
- if (!suppressKeyNotFoundWarning || e.errorCode != KEY_NOT_FOUND) {
- Log.w(TAG, "KeyStore exception", e);
- }
- return null;
- }
- }
-
- public byte[] get(String key, boolean suppressKeyNotFoundWarning) {
- return get(key, UID_SELF, suppressKeyNotFoundWarning);
- }
-
-
- public boolean put(String key, byte[] value, int uid, int flags) {
- return insert(key, value, uid, flags) == NO_ERROR;
- }
-
- public int insert(String key, byte[] value, int uid, int flags) {
- try {
- if (value == null) {
- value = new byte[0];
- }
- int error = mBinder.insert(key, value, uid, flags);
- if (error == KEY_ALREADY_EXISTS) {
- mBinder.del(key, uid);
- error = mBinder.insert(key, value, uid, flags);
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- int delete2(String key, int uid) {
- try {
- return mBinder.del(key, uid);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- public boolean delete(String key, int uid) {
- int ret = delete2(key, uid);
- return ret == NO_ERROR || ret == KEY_NOT_FOUND;
- }
-
+ /** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean delete(String key) {
- return delete(key, UID_SELF);
- }
-
- public boolean contains(String key, int uid) {
- try {
- return mBinder.exist(key, uid) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- public boolean contains(String key) {
- return contains(key, UID_SELF);
- }
-
- /**
- * List all entries in the keystore for {@code uid} starting with {@code prefix}.
- */
- public String[] list(String prefix, int uid) {
- try {
- return mBinder.list(prefix, uid);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (android.os.ServiceSpecificException e) {
- Log.w(TAG, "KeyStore exception", e);
- return null;
- }
+ return false;
}
/**
* List uids of all keys that are auth bound to the current user.
* Only system is allowed to call this method.
+ * @hide
+ * @deprecated This function always returns null.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public int[] listUidsOfAuthBoundKeys() {
- // uids are returned as a list of strings because list of integers
- // as an output parameter is not supported by aidl-cpp.
- List<String> uidsOut = new ArrayList<>();
- try {
- int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
- if (rc != NO_ERROR) {
- Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc));
- return null;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (android.os.ServiceSpecificException e) {
- Log.w(TAG, "KeyStore exception", e);
- return null;
- }
- // Turn list of strings into an array of uid integers.
- return uidsOut.stream().mapToInt(Integer::parseInt).toArray();
- }
-
- public String[] list(String prefix) {
- return list(prefix, UID_SELF);
+ return null;
}
+
/**
- * Attempt to lock the keystore for {@code user}.
- *
- * @param userId Android user to lock.
- * @return whether {@code user}'s keystore was locked.
+ * @hide
+ * @deprecated This function has no effect.
*/
- public boolean lock(int userId) {
- try {
- return mBinder.lock(userId) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- public boolean lock() {
- return lock(UserHandle.myUserId());
- }
-
- /**
- * Attempt to unlock the keystore for {@code user} with the password {@code password}.
- * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or
- * created.
- *
- * @param userId Android user ID to operate on
- * @param password user's keystore password. Should be the most recent value passed to
- * {@link #onUserPasswordChanged} for the user.
- *
- * @return whether the keystore was unlocked.
- */
- public boolean unlock(int userId, String password) {
- try {
- password = password != null ? password : "";
- mError = mBinder.unlock(userId, password);
- return mError == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean unlock(String password) {
- return unlock(UserHandle.getUserId(Process.myUid()), password);
+ return false;
}
/**
- * Check if the keystore for {@code userId} is empty.
+ *
+ * @return
+ * @deprecated This function always returns true.
+ * @hide
*/
- public boolean isEmpty(int userId) {
- try {
- return mBinder.isEmpty(userId) != 0;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public boolean isEmpty() {
- return isEmpty(UserHandle.myUserId());
- }
-
- public String grant(String key, int uid) {
- try {
- String grantAlias = mBinder.grant(key, uid);
- if (grantAlias == "") return null;
- return grantAlias;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- }
- }
-
- public boolean ungrant(String key, int uid) {
- try {
- return mBinder.ungrant(key, uid) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- /**
- * Returns the last modification time of the key in milliseconds since the
- * epoch. Will return -1L if the key could not be found or other error.
- */
- public long getmtime(String key, int uid) {
- try {
- final long millis = mBinder.getmtime(key, uid);
- if (millis == -1L) {
- return -1L;
- }
-
- return millis * 1000L;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return -1L;
- }
- }
-
- public long getmtime(String key) {
- return getmtime(key, UID_SELF);
- }
-
- // TODO: remove this when it's removed from Settings
- public boolean isHardwareBacked() {
- return isHardwareBacked("RSA");
- }
-
- public boolean isHardwareBacked(String keyType) {
- try {
- return mBinder.is_hardware_backed(keyType.toUpperCase(Locale.US)) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- public boolean clearUid(int uid) {
- try {
- if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
- return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
- }
- return mBinder.clear_uid(uid) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- public int getLastError() {
- return mError;
- }
-
- public boolean addRngEntropy(byte[] data, int flags) {
- KeystoreResultPromise promise = new KeystoreResultPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- int errorCode = mBinder.addRngEntropy(promise, data, flags);
- if (errorCode == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture()).getErrorCode() == NO_ERROR;
- } else {
- return false;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- } catch (ExecutionException e) {
- Log.e(TAG, "AddRngEntropy completed with exception", e);
- return false;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- private class KeyCharacteristicsCallbackResult {
- private KeystoreResponse keystoreResponse;
- private KeyCharacteristics keyCharacteristics;
-
- public KeyCharacteristicsCallbackResult(KeystoreResponse keystoreResponse,
- KeyCharacteristics keyCharacteristics) {
- this.keystoreResponse = keystoreResponse;
- this.keyCharacteristics = keyCharacteristics;
- }
-
- public KeystoreResponse getKeystoreResponse() {
- return keystoreResponse;
- }
-
- public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
- this.keystoreResponse = keystoreResponse;
- }
-
- public KeyCharacteristics getKeyCharacteristics() {
- return keyCharacteristics;
- }
-
- public void setKeyCharacteristics(KeyCharacteristics keyCharacteristics) {
- this.keyCharacteristics = keyCharacteristics;
- }
- }
-
- private class KeyCharacteristicsPromise
- extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub
- implements IBinder.DeathRecipient {
- final private CompletableFuture<KeyCharacteristicsCallbackResult> future =
- new CompletableFuture<KeyCharacteristicsCallbackResult>();
- @Override
- public void onFinished(KeystoreResponse keystoreResponse,
- KeyCharacteristics keyCharacteristics)
- throws android.os.RemoteException {
- future.complete(
- new KeyCharacteristicsCallbackResult(keystoreResponse, keyCharacteristics));
- }
- public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() {
- return future;
- }
- @Override
- public void binderDied() {
- future.completeExceptionally(new RemoteException("Keystore died"));
- }
- };
-
- private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
- int flags, KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException {
- KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
- int error = NO_ERROR;
- KeyCharacteristicsCallbackResult result = null;
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
- if (error != NO_ERROR) {
- Log.e(TAG, "generateKeyInternal failed on request " + error);
- return error;
- }
- result = interruptedPreservingGet(promise.getFuture());
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
-
- error = result.getKeystoreResponse().getErrorCode();
- if (error != NO_ERROR) {
- Log.e(TAG, "generateKeyInternal failed on response " + error);
- return error;
- }
- KeyCharacteristics characteristics = result.getKeyCharacteristics();
- if (characteristics == null) {
- Log.e(TAG, "generateKeyInternal got empty key characteristics " + error);
- return SYSTEM_ERROR;
- }
- outCharacteristics.shallowCopyFrom(characteristics);
- return NO_ERROR;
- }
-
- public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
- int flags, KeyCharacteristics outCharacteristics) {
- try {
- entropy = entropy != null ? entropy : new byte[0];
- args = args != null ? args : new KeymasterArguments();
- int error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
- if (error == KEY_ALREADY_EXISTS) {
- mBinder.del(alias, uid);
- error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "generateKey completed with exception", e);
- return SYSTEM_ERROR;
- }
- }
-
- public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags,
- KeyCharacteristics outCharacteristics) {
- return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics);
- }
-
- public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
- int uid, KeyCharacteristics outCharacteristics) {
- KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
- appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
-
- int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
- if (error != NO_ERROR) return error;
-
- KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
- error = result.getKeystoreResponse().getErrorCode();
- if (error != NO_ERROR) return error;
-
- KeyCharacteristics characteristics = result.getKeyCharacteristics();
- if (characteristics == null) return SYSTEM_ERROR;
- outCharacteristics.shallowCopyFrom(characteristics);
- return NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
- return SYSTEM_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
- KeyCharacteristics outCharacteristics) {
- return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics);
- }
-
- private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData,
- int uid, int flags, KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException {
- KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
- mBinder.asBinder().linkToDeath(promise, 0);
- try {
- int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
- if (error != NO_ERROR) return error;
-
- KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
-
- error = result.getKeystoreResponse().getErrorCode();
- if (error != NO_ERROR) return error;
-
- KeyCharacteristics characteristics = result.getKeyCharacteristics();
- if (characteristics == null) return SYSTEM_ERROR;
- outCharacteristics.shallowCopyFrom(characteristics);
- return NO_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
- int uid, int flags, KeyCharacteristics outCharacteristics) {
- try {
- int error = importKeyInternal(alias, args, format, keyData, uid, flags,
- outCharacteristics);
- if (error == KEY_ALREADY_EXISTS) {
- mBinder.del(alias, uid);
- error = importKeyInternal(alias, args, format, keyData, uid, flags,
- outCharacteristics);
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "ImportKey completed with exception", e);
- return SYSTEM_ERROR;
- }
- }
-
- public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
- int flags, KeyCharacteristics outCharacteristics) {
- return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
- }
-
- private String getAlgorithmFromPKCS8(byte[] keyData) {
- try {
- final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
- final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
- final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
- return new AlgorithmId(new ObjectIdentifier(algOid)).getName();
- } catch (IOException e) {
- Log.e(TAG, "getAlgorithmFromPKCS8 Failed to parse key data");
- Log.e(TAG, Log.getStackTraceString(e));
- return null;
- }
- }
-
- private KeymasterArguments makeLegacyArguments(String algorithm) {
- KeymasterArguments args = new KeymasterArguments();
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
- KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(algorithm));
- args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN);
- args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_VERIFY);
- args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
- args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
- args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
- if (algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_RSA)) {
- args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP);
- args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
- args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
- args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PSS);
- }
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384);
- args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512);
- args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, new Date(Long.MAX_VALUE));
- args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, new Date(Long.MAX_VALUE));
- args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, new Date(0));
- return args;
- }
-
- public boolean importKey(String alias, byte[] keyData, int uid, int flags) {
- String algorithm = getAlgorithmFromPKCS8(keyData);
- if (algorithm == null) return false;
- KeymasterArguments args = makeLegacyArguments(algorithm);
- KeyCharacteristics out = new KeyCharacteristics();
- int result = importKey(alias, args, KeymasterDefs.KM_KEY_FORMAT_PKCS8, keyData, uid,
- flags, out);
- if (result != NO_ERROR) {
- Log.e(TAG, Log.getStackTraceString(
- new KeyStoreException(result, "legacy key import failed")));
- return false;
- }
return true;
}
- private int importWrappedKeyInternal(String wrappedKeyAlias, byte[] wrappedKey,
- String wrappingKeyAlias,
- byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
- KeyCharacteristics outCharacteristics)
- throws RemoteException, ExecutionException {
- KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
- mBinder.asBinder().linkToDeath(promise, 0);
- try {
- int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey,
- wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid);
- if (error != NO_ERROR) return error;
-
- KeyCharacteristicsCallbackResult result = interruptedPreservingGet(promise.getFuture());
-
- error = result.getKeystoreResponse().getErrorCode();
- if (error != NO_ERROR) return error;
-
- KeyCharacteristics characteristics = result.getKeyCharacteristics();
- if (characteristics == null) return SYSTEM_ERROR;
- outCharacteristics.shallowCopyFrom(characteristics);
- return NO_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
- String wrappingKeyAlias,
- byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
- KeyCharacteristics outCharacteristics) {
- // TODO b/119217337 uid parameter gets silently ignored.
- try {
- int error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
- maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
- if (error == KEY_ALREADY_EXISTS) {
- mBinder.del(wrappedKeyAlias, UID_SELF);
- error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
- maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "ImportWrappedKey completed with exception", e);
- return SYSTEM_ERROR;
- }
- }
-
- private class ExportKeyPromise
- extends android.security.keystore.IKeystoreExportKeyCallback.Stub
- implements IBinder.DeathRecipient {
- final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>();
- @Override
- public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException {
- future.complete(exportKeyResult);
- }
- public final CompletableFuture<ExportResult> getFuture() {
- return future;
- }
- @Override
- public void binderDied() {
- future.completeExceptionally(new RemoteException("Keystore died"));
- }
- };
-
- public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
- KeymasterBlob appId, int uid) {
- ExportKeyPromise promise = new ExportKeyPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
- appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
- int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
- if (error == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture());
- } else {
- return new ExportResult(error);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (ExecutionException e) {
- Log.e(TAG, "ExportKey completed with exception", e);
- return null;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
- public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
- KeymasterBlob appId) {
- return exportKey(alias, format, clientId, appId, UID_SELF);
- }
-
- private class OperationPromise
- extends android.security.keystore.IKeystoreOperationResultCallback.Stub
- implements IBinder.DeathRecipient {
- final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>();
- @Override
- public void onFinished(OperationResult operationResult) throws android.os.RemoteException {
- future.complete(operationResult);
- }
- public final CompletableFuture<OperationResult> getFuture() {
- return future;
- }
- @Override
- public void binderDied() {
- future.completeExceptionally(new RemoteException("Keystore died"));
- }
- };
-
- public OperationResult begin(String alias, int purpose, boolean pruneable,
- KeymasterArguments args, byte[] entropy, int uid) {
- OperationPromise promise = new OperationPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- args = args != null ? args : new KeymasterArguments();
- entropy = entropy != null ? entropy : new byte[0];
- int errorCode = mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
- entropy, uid);
- if (errorCode == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture());
- } else {
- return new OperationResult(errorCode);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (ExecutionException e) {
- Log.e(TAG, "Begin completed with exception", e);
- return null;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- public OperationResult begin(String alias, int purpose, boolean pruneable,
- KeymasterArguments args, byte[] entropy) {
- entropy = entropy != null ? entropy : new byte[0];
- args = args != null ? args : new KeymasterArguments();
- return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
- }
-
- public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
- OperationPromise promise = new OperationPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- arguments = arguments != null ? arguments : new KeymasterArguments();
- input = input != null ? input : new byte[0];
- int errorCode = mBinder.update(promise, token, arguments, input);
- if (errorCode == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture());
- } else {
- return new OperationResult(errorCode);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (ExecutionException e) {
- Log.e(TAG, "Update completed with exception", e);
- return null;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
/**
- * Android KeyStore finish operation.
- *
- * @param token Authentication token.
- * @param arguments Keymaster arguments
- * @param input Optional additional input data.
- * @param signature Optional signature to be verified.
- * @param entropy Optional additional entropy
- * @return OperationResult that will indicate success or error of the operation.
+ * Forwards the request to clear a UID to Keystore 2.0.
+ * @hide
*/
- public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] input,
- byte[] signature, byte[] entropy) {
- OperationPromise promise = new OperationPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- arguments = arguments != null ? arguments : new KeymasterArguments();
- entropy = entropy != null ? entropy : new byte[0];
- input = input != null ? input : new byte[0];
- signature = signature != null ? signature : new byte[0];
- int errorCode = mBinder.finish(promise, token, arguments, input, signature, entropy);
- if (errorCode == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture());
- } else {
- return new OperationResult(errorCode);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return null;
- } catch (ExecutionException e) {
- Log.e(TAG, "Finish completed with exception", e);
- return null;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
+ public boolean clearUid(int uid) {
+ return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
}
- public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
- return finish(token, arguments, null, signature, null);
- }
-
- private class KeystoreResultPromise
- extends android.security.keystore.IKeystoreResponseCallback.Stub
- implements IBinder.DeathRecipient {
- final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>();
- @Override
- public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException {
- future.complete(keystoreResponse);
- }
- public final CompletableFuture<KeystoreResponse> getFuture() {
- return future;
- }
- @Override
- public void binderDied() {
- future.completeExceptionally(new RemoteException("Keystore died"));
- }
- };
-
- public int abort(IBinder token) {
- KeystoreResultPromise promise = new KeystoreResultPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- int errorCode = mBinder.abort(promise, token);
- if (errorCode == NO_ERROR) {
- return interruptedPreservingGet(promise.getFuture()).getErrorCode();
- } else {
- return errorCode;
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "Abort completed with exception", e);
- return SYSTEM_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
/**
* Add an authentication record to the keystore authorization table.
@@ -1013,191 +136,7 @@
* a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
*/
public int addAuthToken(byte[] authToken) {
- try {
- Authorization.addAuthToken(authToken);
- return mBinder.addAuthToken(authToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- }
- }
-
- /**
- * Notify keystore that a user's password has changed.
- *
- * @param userId the user whose password changed.
- * @param newPassword the new password or "" if the password was removed.
- */
- public boolean onUserPasswordChanged(int userId, String newPassword) {
- // Parcel.cpp doesn't support deserializing null strings and treats them as "". Make that
- // explicit here.
- if (newPassword == null) {
- newPassword = "";
- }
- try {
- return mBinder.onUserPasswordChanged(userId, newPassword) == NO_ERROR;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
- }
-
- /**
- * Notify keystore that a user was added.
- *
- * @param userId the new user.
- * @param parentId the parent of the new user, or -1 if the user has no parent. If parentId is
- * specified then the new user's keystore will be intialized with the same secure lockscreen
- * password as the parent.
- */
- public void onUserAdded(int userId, int parentId) {
- try {
- mBinder.onUserAdded(userId, parentId);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- }
- }
-
- /**
- * Notify keystore that a user was added.
- *
- * @param userId the new user.
- */
- public void onUserAdded(int userId) {
- onUserAdded(userId, -1);
- }
-
- /**
- * Notify keystore that a user was removed.
- *
- * @param userId the removed user.
- */
- public void onUserRemoved(int userId) {
- try {
- mBinder.onUserRemoved(userId);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- }
- }
-
- public boolean onUserPasswordChanged(String newPassword) {
- return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
- }
-
- /**
- * Notify keystore about the latest user locked state. This is to support keyguard-bound key.
- */
- public void onUserLockedStateChanged(int userHandle, boolean locked) {
- try {
- mBinder.onKeyguardVisibilityChanged(locked, userHandle);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to update user locked state " + userHandle, e);
- }
- }
-
- private class KeyAttestationCallbackResult {
- private KeystoreResponse keystoreResponse;
- private KeymasterCertificateChain certificateChain;
-
- public KeyAttestationCallbackResult(KeystoreResponse keystoreResponse,
- KeymasterCertificateChain certificateChain) {
- this.keystoreResponse = keystoreResponse;
- this.certificateChain = certificateChain;
- }
-
- public KeystoreResponse getKeystoreResponse() {
- return keystoreResponse;
- }
-
- public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
- this.keystoreResponse = keystoreResponse;
- }
-
- public KeymasterCertificateChain getCertificateChain() {
- return certificateChain;
- }
-
- public void setCertificateChain(KeymasterCertificateChain certificateChain) {
- this.certificateChain = certificateChain;
- }
- }
-
- private class CertificateChainPromise
- extends android.security.keystore.IKeystoreCertificateChainCallback.Stub
- implements IBinder.DeathRecipient {
- final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>();
- @Override
- public void onFinished(KeystoreResponse keystoreResponse,
- KeymasterCertificateChain certificateChain) throws android.os.RemoteException {
- future.complete(new KeyAttestationCallbackResult(keystoreResponse, certificateChain));
- }
- public final CompletableFuture<KeyAttestationCallbackResult> getFuture() {
- return future;
- }
- @Override
- public void binderDied() {
- future.completeExceptionally(new RemoteException("Keystore died"));
- }
- };
-
-
- public int attestKey(
- String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
- CertificateChainPromise promise = new CertificateChainPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- if (params == null) {
- params = new KeymasterArguments();
- }
- if (outChain == null) {
- outChain = new KeymasterCertificateChain();
- }
- int error = mBinder.attestKey(promise, alias, params);
- if (error != NO_ERROR) return error;
- KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
- error = result.getKeystoreResponse().getErrorCode();
- if (error == NO_ERROR) {
- outChain.shallowCopyFrom(result.getCertificateChain());
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "AttestKey completed with exception", e);
- return SYSTEM_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
- }
-
- public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
- CertificateChainPromise promise = new CertificateChainPromise();
- try {
- mBinder.asBinder().linkToDeath(promise, 0);
- if (params == null) {
- params = new KeymasterArguments();
- }
- if (outChain == null) {
- outChain = new KeymasterCertificateChain();
- }
- int error = mBinder.attestDeviceIds(promise, params);
- if (error != NO_ERROR) return error;
- KeyAttestationCallbackResult result = interruptedPreservingGet(promise.getFuture());
- error = result.getKeystoreResponse().getErrorCode();
- if (error == NO_ERROR) {
- outChain.shallowCopyFrom(result.getCertificateChain());
- }
- return error;
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return SYSTEM_ERROR;
- } catch (ExecutionException e) {
- Log.e(TAG, "AttestDevicdeIds completed with exception", e);
- return SYSTEM_ERROR;
- } finally {
- mBinder.asBinder().unlinkToDeath(promise, 0);
- }
+ return Authorization.addAuthToken(authToken);
}
/**
@@ -1205,77 +144,6 @@
*/
public void onDeviceOffBody() {
AndroidKeyStoreMaintenance.onDeviceOffBody();
- try {
- mBinder.onDeviceOffBody();
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- }
- }
-
- // Keep in sync with confirmationui/1.0/types.hal.
- public static final int CONFIRMATIONUI_OK = 0;
- public static final int CONFIRMATIONUI_CANCELED = 1;
- public static final int CONFIRMATIONUI_ABORTED = 2;
- public static final int CONFIRMATIONUI_OPERATION_PENDING = 3;
- public static final int CONFIRMATIONUI_IGNORED = 4;
- public static final int CONFIRMATIONUI_SYSTEM_ERROR = 5;
- public static final int CONFIRMATIONUI_UNIMPLEMENTED = 6;
- public static final int CONFIRMATIONUI_UNEXPECTED = 7;
- public static final int CONFIRMATIONUI_UIERROR = 0x10000;
- public static final int CONFIRMATIONUI_UIERROR_MISSING_GLYPH = 0x10001;
- public static final int CONFIRMATIONUI_UIERROR_MESSAGE_TOO_LONG = 0x10002;
- public static final int CONFIRMATIONUI_UIERROR_MALFORMED_UTF8_ENCODING = 0x10003;
-
- /**
- * Requests keystore call into the confirmationui HAL to display a prompt.
- *
- * @param listener the binder to use for callbacks.
- * @param promptText the prompt to display.
- * @param extraData extra data / nonce from application.
- * @param locale the locale as a BCP 47 langauge tag.
- * @param uiOptionsAsFlags the UI options to use, as flags.
- * @return one of the {@code CONFIRMATIONUI_*} constants, for
- * example {@code KeyStore.CONFIRMATIONUI_OK}.
- */
- public int presentConfirmationPrompt(IBinder listener, String promptText, byte[] extraData,
- String locale, int uiOptionsAsFlags) {
- try {
- return mBinder.presentConfirmationPrompt(listener, promptText, extraData, locale,
- uiOptionsAsFlags);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return CONFIRMATIONUI_SYSTEM_ERROR;
- }
- }
-
- /**
- * Requests keystore call into the confirmationui HAL to cancel displaying a prompt.
- *
- * @param listener the binder passed to the {@link #presentConfirmationPrompt} method.
- * @return one of the {@code CONFIRMATIONUI_*} constants, for
- * example {@code KeyStore.CONFIRMATIONUI_OK}.
- */
- public int cancelConfirmationPrompt(IBinder listener) {
- try {
- return mBinder.cancelConfirmationPrompt(listener);
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return CONFIRMATIONUI_SYSTEM_ERROR;
- }
- }
-
- /**
- * Requests keystore to check if the confirmationui HAL is available.
- *
- * @return whether the confirmationUI HAL is available.
- */
- public boolean isConfirmationPromptSupported() {
- try {
- return mBinder.isConfirmationPromptSupported();
- } catch (RemoteException e) {
- Log.w(TAG, "Cannot connect to keystore", e);
- return false;
- }
}
/**
@@ -1284,143 +152,6 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static KeyStoreException getKeyStoreException(int errorCode) {
- if (errorCode > 0) {
- // KeyStore layer error
- switch (errorCode) {
- case NO_ERROR:
- return new KeyStoreException(errorCode, "OK");
- case LOCKED:
- return new KeyStoreException(errorCode, "User authentication required");
- case UNINITIALIZED:
- return new KeyStoreException(errorCode, "Keystore not initialized");
- case SYSTEM_ERROR:
- return new KeyStoreException(errorCode, "System error");
- case PERMISSION_DENIED:
- return new KeyStoreException(errorCode, "Permission denied");
- case KEY_NOT_FOUND:
- return new KeyStoreException(errorCode, "Key not found");
- case VALUE_CORRUPTED:
- return new KeyStoreException(errorCode, "Key blob corrupted");
- case OP_AUTH_NEEDED:
- return new KeyStoreException(errorCode, "Operation requires authorization");
- case KEY_PERMANENTLY_INVALIDATED:
- return new KeyStoreException(errorCode, "Key permanently invalidated");
- default:
- return new KeyStoreException(errorCode, String.valueOf(errorCode));
- }
- } else {
- // Keymaster layer error
- switch (errorCode) {
- case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
- // The name of this parameter significantly differs between Keymaster and
- // framework APIs. Use the framework wording to make life easier for developers.
- return new KeyStoreException(errorCode,
- "Invalid user authentication validity duration");
- default:
- return new KeyStoreException(errorCode,
- KeymasterDefs.getErrorMessage(errorCode));
- }
- }
- }
-
- /**
- * Returns an {@link InvalidKeyException} corresponding to the provided
- * {@link KeyStoreException}.
- */
- public InvalidKeyException getInvalidKeyException(
- String keystoreKeyAlias, int uid, KeyStoreException e) {
- switch (e.getErrorCode()) {
- case LOCKED:
- return new UserNotAuthenticatedException();
- case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
- return new KeyExpiredException();
- case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
- return new KeyNotYetValidException();
- case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
- case OP_AUTH_NEEDED:
- {
- // We now need to determine whether the key/operation can become usable if user
- // authentication is performed, or whether it can never become usable again.
- // User authentication requirements are contained in the key's characteristics. We
- // need to check whether these requirements can be be satisfied by asking the user
- // to authenticate.
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int getKeyCharacteristicsErrorCode =
- getKeyCharacteristics(keystoreKeyAlias, null, null, uid,
- keyCharacteristics);
- if (getKeyCharacteristicsErrorCode != NO_ERROR) {
- return new InvalidKeyException(
- "Failed to obtained key characteristics",
- getKeyStoreException(getKeyCharacteristicsErrorCode));
- }
- List<BigInteger> keySids =
- keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
- if (keySids.isEmpty()) {
- // Key is not bound to any SIDs -- no amount of authentication will help here.
- return new KeyPermanentlyInvalidatedException();
- }
- long rootSid = GateKeeper.getSecureUserId();
- if ((rootSid != 0) && (keySids.contains(KeymasterArguments.toUint64(rootSid)))) {
- // One of the key's SIDs is the current root SID -- user can be authenticated
- // against that SID.
- return new UserNotAuthenticatedException();
- }
-
- final BiometricManager bm = mContext.getSystemService(BiometricManager.class);
- long[] biometricSids = bm.getAuthenticatorIds();
-
- // The key must contain every biometric SID. This is because the current API surface
- // treats all biometrics (capable of keystore integration) equally. e.g. if the
- // device has multiple keystore-capable sensors, and one of the sensor's SIDs
- // changed, 1) there is no way for a developer to specify authentication with a
- // specific sensor (the one that hasn't changed), and 2) currently the only
- // signal to developers is the UserNotAuthenticatedException, which doesn't
- // indicate a specific sensor.
- boolean canUnlockViaBiometrics = true;
- for (long sid : biometricSids) {
- if (!keySids.contains(KeymasterArguments.toUint64(sid))) {
- canUnlockViaBiometrics = false;
- break;
- }
- }
-
- if (canUnlockViaBiometrics) {
- // All of the biometric SIDs are contained in the key's SIDs.
- return new UserNotAuthenticatedException();
- }
-
- // None of the key's SIDs can ever be authenticated
- return new KeyPermanentlyInvalidatedException();
- }
- case UNINITIALIZED:
- return new KeyPermanentlyInvalidatedException();
- default:
- return new InvalidKeyException("Keystore operation failed", e);
- }
- }
-
- /**
- * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
- * code.
- */
- public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid,
- int errorCode) {
- return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
- }
-
- private static <R> R interruptedPreservingGet(CompletableFuture<R> future)
- throws ExecutionException {
- boolean wasInterrupted = false;
- while (true) {
- try {
- R result = future.get();
- if (wasInterrupted) {
- Thread.currentThread().interrupt();
- }
- return result;
- } catch (InterruptedException e) {
- wasInterrupted = true;
- }
- }
+ return new KeyStoreException(-10000, "Should not be called.");
}
}
diff --git a/keystore/java/android/security/LegacyVpnProfileStore.java b/keystore/java/android/security/LegacyVpnProfileStore.java
index 41cfb27..1d2738e 100644
--- a/keystore/java/android/security/LegacyVpnProfileStore.java
+++ b/keystore/java/android/security/LegacyVpnProfileStore.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.vpnprofilestore.IVpnProfileStore;
import android.util.Log;
@@ -53,13 +52,8 @@
*/
public static boolean put(@NonNull String alias, @NonNull byte[] profile) {
try {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- getService().put(alias, profile);
- return true;
- } else {
- return KeyStore.getInstance().put(
- alias, profile, KeyStore.UID_SELF, 0);
- }
+ getService().put(alias, profile);
+ return true;
} catch (Exception e) {
Log.e(TAG, "Failed to put vpn profile.", e);
return false;
@@ -77,11 +71,7 @@
*/
public static byte[] get(@NonNull String alias) {
try {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- return getService().get(alias);
- } else {
- return KeyStore.getInstance().get(alias, true /* suppressKeyNotFoundWarning */);
- }
+ return getService().get(alias);
} catch (ServiceSpecificException e) {
if (e.errorCode != PROFILE_NOT_FOUND) {
Log.e(TAG, "Failed to get vpn profile.", e);
@@ -100,12 +90,8 @@
*/
public static boolean remove(@NonNull String alias) {
try {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- getService().remove(alias);
- return true;
- } else {
- return KeyStore.getInstance().delete(alias);
- }
+ getService().remove(alias);
+ return true;
} catch (ServiceSpecificException e) {
if (e.errorCode != PROFILE_NOT_FOUND) {
Log.e(TAG, "Failed to remove vpn profile.", e);
@@ -124,16 +110,11 @@
*/
public static @NonNull String[] list(@NonNull String prefix) {
try {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- final String[] aliases = getService().list(prefix);
- for (int i = 0; i < aliases.length; ++i) {
- aliases[i] = aliases[i].substring(prefix.length());
- }
- return aliases;
- } else {
- final String[] result = KeyStore.getInstance().list(prefix);
- return result != null ? result : new String[0];
+ final String[] aliases = getService().list(prefix);
+ for (int i = 0; i < aliases.length; ++i) {
+ aliases[i] = aliases[i].substring(prefix.length());
}
+ return aliases;
} catch (Exception e) {
Log.e(TAG, "Failed to list vpn profiles.", e);
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java
deleted file mode 100644
index 01fd062..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.ProviderException;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-import java.util.Arrays;
-
-import javax.crypto.CipherSpi;
-import javax.crypto.spec.IvParameterSpec;
-
-/**
- * Base class for Android Keystore 3DES {@link CipherSpi} implementations.
- *
- * @hide
- */
-public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
-
- private static final int BLOCK_SIZE_BYTES = 8;
-
- private final int mKeymasterBlockMode;
- private final int mKeymasterPadding;
- /** Whether this transformation requires an IV. */
- private final boolean mIvRequired;
-
- private byte[] mIv;
-
- /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
- private boolean mIvHasBeenUsed;
-
- AndroidKeyStore3DESCipherSpi(
- int keymasterBlockMode,
- int keymasterPadding,
- boolean ivRequired) {
- mKeymasterBlockMode = keymasterBlockMode;
- mKeymasterPadding = keymasterPadding;
- mIvRequired = ivRequired;
- }
-
- abstract static class ECB extends AndroidKeyStore3DESCipherSpi {
- protected ECB(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
- }
-
- public static class NoPadding extends ECB {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
- }
-
- public static class PKCS7Padding extends ECB {
- public PKCS7Padding() {
- super(KeymasterDefs.KM_PAD_PKCS7);
- }
- }
- }
-
- abstract static class CBC extends AndroidKeyStore3DESCipherSpi {
- protected CBC(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
- }
-
- public static class NoPadding extends CBC {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
- }
-
- public static class PKCS7Padding extends CBC {
- public PKCS7Padding() {
- super(KeymasterDefs.KM_PAD_PKCS7);
- }
- }
- }
-
- @Override
- protected void initKey(int i, Key key) throws InvalidKeyException {
- if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeyException(
- "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
- }
- if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException(
- "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
- KeyProperties.KEY_ALGORITHM_3DES + " supported");
- }
- setKey((AndroidKeyStoreSecretKey) key);
- }
-
- @Override
- protected int engineGetBlockSize() {
- return BLOCK_SIZE_BYTES;
- }
-
- @Override
- protected int engineGetOutputSize(int inputLen) {
- return inputLen + 3 * BLOCK_SIZE_BYTES;
- }
-
- @Override
- protected final byte[] engineGetIV() {
- return ArrayUtils.cloneIfNotEmpty(mIv);
- }
-
- @Override
- protected AlgorithmParameters engineGetParameters() {
- if (!mIvRequired) {
- return null;
- }
- if ((mIv != null) && (mIv.length > 0)) {
- try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("DESede");
- params.init(new IvParameterSpec(mIv));
- return params;
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain 3DES AlgorithmParameters", e);
- } catch (InvalidParameterSpecException e) {
- throw new ProviderException(
- "Failed to initialize 3DES AlgorithmParameters with an IV",
- e);
- }
- }
- return null;
- }
-
- @Override
- protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
- if (!mIvRequired) {
- return;
- }
-
- // IV is used
- if (!isEncrypting()) {
- throw new InvalidKeyException("IV required when decrypting"
- + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
- }
- }
-
- @Override
- protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
- if (!mIvRequired) {
- if (params != null) {
- throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
- }
- return;
- }
-
- // IV is used
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException(
- "IvParameterSpec must be provided when decrypting");
- }
- return;
- }
- if (!(params instanceof IvParameterSpec)) {
- throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
- }
- mIv = ((IvParameterSpec) params).getIV();
- if (mIv == null) {
- throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
- }
- }
-
- @Override
- protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
- if (!mIvRequired) {
- if (params != null) {
- throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
- }
- return;
- }
-
- // IV is used
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV required when decrypting"
- + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
- }
- return;
- }
-
- if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
- + ". Supported: DESede");
- }
-
- IvParameterSpec ivSpec;
- try {
- ivSpec = params.getParameterSpec(IvParameterSpec.class);
- } catch (InvalidParameterSpecException e) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV required when decrypting"
- + ", but not found in parameters: " + params, e);
- }
- mIv = null;
- return;
- }
- mIv = ivSpec.getIV();
- if (mIv == null) {
- throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
- }
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
- // IV will need to be generated
- return BLOCK_SIZE_BYTES;
- }
-
- return 0;
- }
-
- @Override
- protected int getAdditionalEntropyAmountForFinish() {
- return 0;
- }
-
- @Override
- protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
- if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
- // IV is being reused for encryption: this violates security best practices.
- throw new IllegalStateException(
- "IV has already been used. Reusing IV in encryption mode violates security best"
- + " practices.");
- }
-
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
- if ((mIvRequired) && (mIv != null)) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
- }
- }
-
- @Override
- protected void loadAlgorithmSpecificParametersFromBeginResult(
- KeymasterArguments keymasterArgs) {
- mIvHasBeenUsed = true;
-
- // NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
- }
-
- if (mIvRequired) {
- if (mIv == null) {
- mIv = returnedIv;
- } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new ProviderException("IV in use differs from provided IV");
- }
- } else {
- if (returnedIv != null) {
- throw new ProviderException(
- "IV in use despite IV not being used by this transformation");
- }
- }
- }
-
- @Override
- protected final void resetAll() {
- mIv = null;
- mIvHasBeenUsed = false;
- super.resetAll();
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
deleted file mode 100644
index feb6101..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ /dev/null
@@ -1,449 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-import android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.Stream;
-
-import libcore.util.EmptyArray;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.ProviderException;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-import java.util.Arrays;
-
-import javax.crypto.CipherSpi;
-import javax.crypto.spec.GCMParameterSpec;
-
-/**
- * Base class for Android Keystore authenticated AES {@link CipherSpi} implementations.
- *
- * @hide
- */
-abstract class AndroidKeyStoreAuthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
-
- abstract static class GCM extends AndroidKeyStoreAuthenticatedAESCipherSpi {
- static final int MIN_SUPPORTED_TAG_LENGTH_BITS = 96;
- private static final int MAX_SUPPORTED_TAG_LENGTH_BITS = 128;
- private static final int DEFAULT_TAG_LENGTH_BITS = 128;
- private static final int IV_LENGTH_BYTES = 12;
-
- private int mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
-
- GCM(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_GCM, keymasterPadding);
- }
-
- @Override
- protected final void resetAll() {
- mTagLengthBits = DEFAULT_TAG_LENGTH_BITS;
- super.resetAll();
- }
-
- @Override
- protected final void resetWhilePreservingInitState() {
- super.resetWhilePreservingInitState();
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {
- if (!isEncrypting()) {
- throw new InvalidKeyException("IV required when decrypting"
- + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
- }
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
- // IV is used
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException(
- "GCMParameterSpec must be provided when decrypting");
- }
- return;
- }
- if (!(params instanceof GCMParameterSpec)) {
- throw new InvalidAlgorithmParameterException("Only GCMParameterSpec supported");
- }
- GCMParameterSpec spec = (GCMParameterSpec) params;
- byte[] iv = spec.getIV();
- if (iv == null) {
- throw new InvalidAlgorithmParameterException("Null IV in GCMParameterSpec");
- } else if (iv.length != IV_LENGTH_BYTES) {
- throw new InvalidAlgorithmParameterException("Unsupported IV length: "
- + iv.length + " bytes. Only " + IV_LENGTH_BYTES
- + " bytes long IV supported");
- }
- int tagLengthBits = spec.getTLen();
- if ((tagLengthBits < MIN_SUPPORTED_TAG_LENGTH_BITS)
- || (tagLengthBits > MAX_SUPPORTED_TAG_LENGTH_BITS)
- || ((tagLengthBits % 8) != 0)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported tag length: " + tagLengthBits + " bits"
- + ". Supported lengths: 96, 104, 112, 120, 128");
- }
- setIv(iv);
- mTagLengthBits = tagLengthBits;
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters(AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV required when decrypting"
- + ". Use GCMParameterSpec or GCM AlgorithmParameters to provide it.");
- }
- return;
- }
-
- if (!"GCM".equalsIgnoreCase(params.getAlgorithm())) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
- + ". Supported: GCM");
- }
-
- GCMParameterSpec spec;
- try {
- spec = params.getParameterSpec(GCMParameterSpec.class);
- } catch (InvalidParameterSpecException e) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV and tag length required when"
- + " decrypting, but not found in parameters: " + params, e);
- }
- setIv(null);
- return;
- }
- initAlgorithmSpecificParameters(spec);
- }
-
- @Nullable
- @Override
- protected final AlgorithmParameters engineGetParameters() {
- byte[] iv = getIv();
- if ((iv != null) && (iv.length > 0)) {
- try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("GCM");
- params.init(new GCMParameterSpec(mTagLengthBits, iv));
- return params;
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain GCM AlgorithmParameters", e);
- } catch (InvalidParameterSpecException e) {
- throw new ProviderException(
- "Failed to initialize GCM AlgorithmParameters", e);
- }
- }
- return null;
- }
-
- @NonNull
- @Override
- protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
- KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
- new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken), 0);
- if (isEncrypting()) {
- return streamer;
- } else {
- // When decrypting, to avoid leaking unauthenticated plaintext, do not return any
- // plaintext before ciphertext is authenticated by KeyStore.finish.
- return new BufferAllOutputUntilDoFinalStreamer(streamer);
- }
- }
-
- @NonNull
- @Override
- protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
- return new KeyStoreCryptoOperationChunkedStreamer(
- new AdditionalAuthenticationDataStream(keyStore, operationToken), 0);
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- if ((getIv() == null) && (isEncrypting())) {
- // IV will need to be generated
- return IV_LENGTH_BYTES;
- }
-
- return 0;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForFinish() {
- return 0;
- }
-
- @Override
- protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
- }
-
- protected final int getTagLengthBits() {
- return mTagLengthBits;
- }
-
- public static final class NoPadding extends GCM {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
-
- @Override
- protected final int engineGetOutputSize(int inputLen) {
- int tagLengthBytes = (getTagLengthBits() + 7) / 8;
- long result;
- if (isEncrypting()) {
- result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
- + tagLengthBytes;
- } else {
- result = getConsumedInputSizeBytes() - getProducedOutputSizeBytes() + inputLen
- - tagLengthBytes;
- }
- if (result < 0) {
- return 0;
- } else if (result > Integer.MAX_VALUE) {
- return Integer.MAX_VALUE;
- }
- return (int) result;
- }
- }
- }
-
- private static final int BLOCK_SIZE_BYTES = 16;
-
- private final int mKeymasterBlockMode;
- private final int mKeymasterPadding;
-
- private byte[] mIv;
-
- /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
- private boolean mIvHasBeenUsed;
-
- AndroidKeyStoreAuthenticatedAESCipherSpi(
- int keymasterBlockMode,
- int keymasterPadding) {
- mKeymasterBlockMode = keymasterBlockMode;
- mKeymasterPadding = keymasterPadding;
- }
-
- @Override
- protected void resetAll() {
- mIv = null;
- mIvHasBeenUsed = false;
- super.resetAll();
- }
-
- @Override
- protected final void initKey(int opmode, Key key) throws InvalidKeyException {
- if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeyException(
- "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
- }
- if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException(
- "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
- KeyProperties.KEY_ALGORITHM_AES + " supported");
- }
- setKey((AndroidKeyStoreSecretKey) key);
- }
-
- @Override
- protected void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- if ((isEncrypting()) && (mIvHasBeenUsed)) {
- // IV is being reused for encryption: this violates security best practices.
- throw new IllegalStateException(
- "IV has already been used. Reusing IV in encryption mode violates security best"
- + " practices.");
- }
-
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
- if (mIv != null) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
- }
- }
-
- @Override
- protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
- mIvHasBeenUsed = true;
-
- // NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
- }
-
- if (mIv == null) {
- mIv = returnedIv;
- } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new ProviderException("IV in use differs from provided IV");
- }
- }
-
- @Override
- protected final int engineGetBlockSize() {
- return BLOCK_SIZE_BYTES;
- }
-
- @Override
- protected final byte[] engineGetIV() {
- return ArrayUtils.cloneIfNotEmpty(mIv);
- }
-
- protected void setIv(byte[] iv) {
- mIv = iv;
- }
-
- protected byte[] getIv() {
- return mIv;
- }
-
- /**
- * {@link KeyStoreCryptoOperationStreamer} which buffers all output until {@code doFinal} from
- * which it returns all output in one go, provided {@code doFinal} succeeds.
- */
- private static class BufferAllOutputUntilDoFinalStreamer
- implements KeyStoreCryptoOperationStreamer {
-
- private final KeyStoreCryptoOperationStreamer mDelegate;
- private ByteArrayOutputStream mBufferedOutput = new ByteArrayOutputStream();
- private long mProducedOutputSizeBytes;
-
- private BufferAllOutputUntilDoFinalStreamer(KeyStoreCryptoOperationStreamer delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public byte[] update(byte[] input, int inputOffset, int inputLength)
- throws KeyStoreException {
- byte[] output = mDelegate.update(input, inputOffset, inputLength);
- if (output != null) {
- try {
- mBufferedOutput.write(output);
- } catch (IOException e) {
- throw new ProviderException("Failed to buffer output", e);
- }
- }
- return EmptyArray.BYTE;
- }
-
- @Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
- byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
- byte[] output = mDelegate.doFinal(input, inputOffset, inputLength, signature,
- additionalEntropy);
- if (output != null) {
- try {
- mBufferedOutput.write(output);
- } catch (IOException e) {
- throw new ProviderException("Failed to buffer output", e);
- }
- }
- byte[] result = mBufferedOutput.toByteArray();
- mBufferedOutput.reset();
- mProducedOutputSizeBytes += result.length;
- return result;
- }
-
- @Override
- public long getConsumedInputSizeBytes() {
- return mDelegate.getConsumedInputSizeBytes();
- }
-
- @Override
- public long getProducedOutputSizeBytes() {
- return mProducedOutputSizeBytes;
- }
- }
-
- /**
- * Additional Authentication Data (AAD) stream via a KeyStore streaming operation. This stream
- * sends AAD into the KeyStore.
- */
- private static class AdditionalAuthenticationDataStream implements Stream {
-
- private final KeyStore mKeyStore;
- private final IBinder mOperationToken;
-
- private AdditionalAuthenticationDataStream(KeyStore keyStore, IBinder operationToken) {
- mKeyStore = keyStore;
- mOperationToken = operationToken;
- }
-
- @Override
- public OperationResult update(byte[] input) {
- KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_ASSOCIATED_DATA, input);
-
- // KeyStore does not reflect AAD in inputConsumed, but users of Stream rely on this
- // field. We fix this discrepancy here. KeyStore.update contract is that all of AAD
- // has been consumed if the method succeeds.
- OperationResult result = mKeyStore.update(mOperationToken, keymasterArgs, null);
- if (result.resultCode == KeyStore.NO_ERROR) {
- result = new OperationResult(
- result.resultCode,
- result.token,
- result.operationHandle,
- input.length, // inputConsumed
- result.output,
- result.outParams);
- }
- return result;
- }
-
- @Override
- public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) {
- if ((additionalEntropy != null) && (additionalEntropy.length > 0)) {
- throw new ProviderException("AAD stream does not support additional entropy");
- }
- return new OperationResult(
- KeyStore.NO_ERROR,
- mOperationToken,
- 0, // operation handle -- nobody cares about this being returned from finish
- 0, // inputConsumed
- EmptyArray.BYTE, // output
- new KeymasterArguments() // additional params returned by finish
- );
- }
- }
-}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
deleted file mode 100644
index 5730234..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.Provider;
-
-/**
- * {@link Provider} of JCA crypto operations operating on Android KeyStore keys.
- *
- * <p>This provider was separated out of {@link AndroidKeyStoreProvider} to work around the issue
- * that Bouncy Castle provider incorrectly declares that it accepts arbitrary keys (incl. Android
- * KeyStore ones). This causes JCA to select the Bouncy Castle's implementation of JCA crypto
- * operations for Android KeyStore keys unless Android KeyStore's own implementations are installed
- * as higher-priority than Bouncy Castle ones. The purpose of this provider is to do just that: to
- * offer crypto operations operating on Android KeyStore keys and to be installed at higher priority
- * than the Bouncy Castle provider.
- *
- * <p>Once Bouncy Castle provider is fixed, this provider can be merged into the
- * {@code AndroidKeyStoreProvider}.
- *
- * @hide
- */
-public class AndroidKeyStoreBCWorkaroundProvider extends Provider {
-
- // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
- // classes when this provider is instantiated and installed early on during each app's
- // initialization process.
-
- private static final String PACKAGE_NAME = "android.security.keystore";
- private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
- PACKAGE_NAME + ".AndroidKeyStoreSecretKey";
- private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME =
- PACKAGE_NAME + ".AndroidKeyStorePrivateKey";
- private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME =
- PACKAGE_NAME + ".AndroidKeyStorePublicKey";
-
- private static final String DESEDE_SYSTEM_PROPERTY = "ro.hardware.keystore_desede";
-
- /** @hide */
- public AndroidKeyStoreBCWorkaroundProvider() {
- this("AndroidKeyStoreBCWorkaround");
- }
-
- /** @hide **/
- public AndroidKeyStoreBCWorkaroundProvider(String providerName) {
- super(providerName,
- 1.0,
- "Android KeyStore security provider to work around Bouncy Castle");
-
- // --------------------- javax.crypto.Mac
- putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1");
- put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
- put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
- put("Alg.Alias.Mac.HMAC/SHA1", "HmacSHA1");
-
- putMacImpl("HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA224");
- put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA224");
- put("Alg.Alias.Mac.HMAC-SHA224", "HmacSHA224");
- put("Alg.Alias.Mac.HMAC/SHA224", "HmacSHA224");
-
- putMacImpl("HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA256");
- put("Alg.Alias.Mac.1.2.840.113549.2.9", "HmacSHA256");
- put("Alg.Alias.Mac.HMAC-SHA256", "HmacSHA256");
- put("Alg.Alias.Mac.HMAC/SHA256", "HmacSHA256");
-
- putMacImpl("HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA384");
- put("Alg.Alias.Mac.1.2.840.113549.2.10", "HmacSHA384");
- put("Alg.Alias.Mac.HMAC-SHA384", "HmacSHA384");
- put("Alg.Alias.Mac.HMAC/SHA384", "HmacSHA384");
-
- putMacImpl("HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA512");
- put("Alg.Alias.Mac.1.2.840.113549.2.11", "HmacSHA512");
- put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
- put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
-
- // --------------------- javax.crypto.Cipher
- putSymmetricCipherImpl("AES/ECB/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding");
- putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
- PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$PKCS7Padding");
-
- putSymmetricCipherImpl("AES/CBC/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$NoPadding");
- putSymmetricCipherImpl("AES/CBC/PKCS7Padding",
- PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CBC$PKCS7Padding");
-
- putSymmetricCipherImpl("AES/CTR/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
-
- if ("true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY))) {
- putSymmetricCipherImpl("DESede/CBC/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding");
- putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
- PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding");
-
- putSymmetricCipherImpl("DESede/ECB/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding");
- putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
- PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding");
- }
-
- putSymmetricCipherImpl("AES/GCM/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding");
-
- putAsymmetricCipherImpl("RSA/ECB/NoPadding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding");
- put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
- putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding");
- put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPPadding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
- "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
- "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
- "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
- "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
- putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
- PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
- put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
- "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
-
- // --------------------- java.security.Signature
- putSignatureImpl("NONEwithRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$NONEWithPKCS1Padding");
-
- putSignatureImpl("MD5withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$MD5WithPKCS1Padding");
- put("Alg.Alias.Signature.MD5WithRSAEncryption", "MD5withRSA");
- put("Alg.Alias.Signature.MD5/RSA", "MD5withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.2.5with1.2.840.113549.1.1.1", "MD5withRSA");
-
- putSignatureImpl("SHA1withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPKCS1Padding");
- put("Alg.Alias.Signature.SHA1WithRSAEncryption", "SHA1withRSA");
- put("Alg.Alias.Signature.SHA1/RSA", "SHA1withRSA");
- put("Alg.Alias.Signature.SHA-1/RSA", "SHA1withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.1", "SHA1withRSA");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.113549.1.1.5", "SHA1withRSA");
- put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA");
-
- putSignatureImpl("SHA224withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPKCS1Padding");
- put("Alg.Alias.Signature.SHA224WithRSAEncryption", "SHA224withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA224withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.1",
- "SHA224withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.113549.1.1.11",
- "SHA224withRSA");
-
- putSignatureImpl("SHA256withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPKCS1Padding");
- put("Alg.Alias.Signature.SHA256WithRSAEncryption", "SHA256withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.1",
- "SHA256withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.113549.1.1.11",
- "SHA256withRSA");
-
- putSignatureImpl("SHA384withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPKCS1Padding");
- put("Alg.Alias.Signature.SHA384WithRSAEncryption", "SHA384withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.113549.1.1.1",
- "SHA384withRSA");
-
- putSignatureImpl("SHA512withRSA",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPKCS1Padding");
- put("Alg.Alias.Signature.SHA512WithRSAEncryption", "SHA512withRSA");
- put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.113549.1.1.1",
- "SHA512withRSA");
-
- putSignatureImpl("SHA1withRSA/PSS",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA1WithPSSPadding");
- putSignatureImpl("SHA224withRSA/PSS",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA224WithPSSPadding");
- putSignatureImpl("SHA256withRSA/PSS",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA256WithPSSPadding");
- putSignatureImpl("SHA384withRSA/PSS",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA384WithPSSPadding");
- putSignatureImpl("SHA512withRSA/PSS",
- PACKAGE_NAME + ".AndroidKeyStoreRSASignatureSpi$SHA512WithPSSPadding");
-
- putSignatureImpl("NONEwithECDSA",
- PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$NONE");
-
- putSignatureImpl("SHA1withECDSA", PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA1");
- put("Alg.Alias.Signature.ECDSA", "SHA1withECDSA");
- put("Alg.Alias.Signature.ECDSAwithSHA1", "SHA1withECDSA");
- // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA1(1)
- put("Alg.Alias.Signature.1.2.840.10045.4.1", "SHA1withECDSA");
- put("Alg.Alias.Signature.1.3.14.3.2.26with1.2.840.10045.2.1", "SHA1withECDSA");
-
- // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
- putSignatureImpl("SHA224withECDSA",
- PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA224");
- // ecdsa-with-SHA224(1)
- put("Alg.Alias.Signature.1.2.840.10045.4.3.1", "SHA224withECDSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.4with1.2.840.10045.2.1", "SHA224withECDSA");
-
- // iso(1) member-body(2) us(840) ansi-x962(10045) signatures(4) ecdsa-with-SHA2(3)
- putSignatureImpl("SHA256withECDSA",
- PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA256");
- // ecdsa-with-SHA256(2)
- put("Alg.Alias.Signature.1.2.840.10045.4.3.2", "SHA256withECDSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.1with1.2.840.10045.2.1", "SHA256withECDSA");
-
- putSignatureImpl("SHA384withECDSA",
- PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA384");
- // ecdsa-with-SHA384(3)
- put("Alg.Alias.Signature.1.2.840.10045.4.3.3", "SHA384withECDSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.2with1.2.840.10045.2.1", "SHA384withECDSA");
-
- putSignatureImpl("SHA512withECDSA",
- PACKAGE_NAME + ".AndroidKeyStoreECDSASignatureSpi$SHA512");
- // ecdsa-with-SHA512(4)
- put("Alg.Alias.Signature.1.2.840.10045.4.3.4", "SHA512withECDSA");
- put("Alg.Alias.Signature.2.16.840.1.101.3.4.2.3with1.2.840.10045.2.1", "SHA512withECDSA");
- }
-
- private void putMacImpl(String algorithm, String implClass) {
- put("Mac." + algorithm, implClass);
- put("Mac." + algorithm + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
- }
-
- private void putSymmetricCipherImpl(String transformation, String implClass) {
- put("Cipher." + transformation, implClass);
- put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
- }
-
- private void putAsymmetricCipherImpl(String transformation, String implClass) {
- put("Cipher." + transformation, implClass);
- put("Cipher." + transformation + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
- }
-
- private void putSignatureImpl(String algorithm, String implClass) {
- put("Signature." + algorithm, implClass);
- put("Signature." + algorithm + " SupportedKeyClasses",
- KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
- }
-
- public static String[] getSupportedEcdsaSignatureDigests() {
- return new String[] {"NONE", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
- }
-
- public static String[] getSupportedRsaSignatureWithPkcs1PaddingDigests() {
- return new String[] {"NONE", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"};
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
deleted file mode 100644
index ccc3153..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ /dev/null
@@ -1,920 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-
-import libcore.util.EmptyArray;
-
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.security.AlgorithmParameters;
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.ProviderException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.CipherSpi;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * Base class for {@link CipherSpi} implementations of Android KeyStore backed ciphers.
- *
- * @hide
- */
-abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
- private final KeyStore mKeyStore;
-
- // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
- // doFinal finishes.
- private boolean mEncrypting;
- private int mKeymasterPurposeOverride = -1;
- private AndroidKeyStoreKey mKey;
- private SecureRandom mRng;
-
- /**
- * Token referencing this operation inside keystore service. It is initialized by
- * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and on some error
- * conditions in between.
- */
- private IBinder mOperationToken;
- private long mOperationHandle;
- private KeyStoreCryptoOperationStreamer mMainDataStreamer;
- private KeyStoreCryptoOperationStreamer mAdditionalAuthenticationDataStreamer;
- private boolean mAdditionalAuthenticationDataStreamerClosed;
-
- /**
- * Encountered exception which could not be immediately thrown because it was encountered inside
- * a method that does not throw checked exception. This exception will be thrown from
- * {@code engineDoFinal}. Once such an exception is encountered, {@code engineUpdate} and
- * {@code engineDoFinal} start ignoring input data.
- */
- private Exception mCachedException;
-
- AndroidKeyStoreCipherSpiBase() {
- mKeyStore = KeyStore.getInstance();
- }
-
- @Override
- protected final void engineInit(int opmode, Key key, SecureRandom random)
- throws InvalidKeyException {
- resetAll();
-
- boolean success = false;
- try {
- init(opmode, key, random);
- initAlgorithmSpecificParameters();
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidAlgorithmParameterException e) {
- throw new InvalidKeyException(e);
- }
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- @Override
- protected final void engineInit(int opmode, Key key, AlgorithmParameters params,
- SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
- resetAll();
-
- boolean success = false;
- try {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- @Override
- protected final void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
- SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
- resetAll();
-
- boolean success = false;
- try {
- init(opmode, key, random);
- initAlgorithmSpecificParameters(params);
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
- switch (opmode) {
- case Cipher.ENCRYPT_MODE:
- case Cipher.WRAP_MODE:
- mEncrypting = true;
- break;
- case Cipher.DECRYPT_MODE:
- case Cipher.UNWRAP_MODE:
- mEncrypting = false;
- break;
- default:
- throw new InvalidParameterException("Unsupported opmode: " + opmode);
- }
- initKey(opmode, key);
- if (mKey == null) {
- throw new ProviderException("initKey did not initialize the key");
- }
- mRng = random;
- }
-
- /**
- * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
- * cipher instance.
- *
- * <p>Subclasses storing additional state should override this method, reset the additional
- * state, and then chain to superclass.
- */
- @CallSuper
- protected void resetAll() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mEncrypting = false;
- mKeymasterPurposeOverride = -1;
- mKey = null;
- mRng = null;
- mOperationToken = null;
- mOperationHandle = 0;
- mMainDataStreamer = null;
- mAdditionalAuthenticationDataStreamer = null;
- mAdditionalAuthenticationDataStreamerClosed = false;
- mCachedException = null;
- }
-
- /**
- * Resets this cipher while preserving the initialized state. This must be equivalent to
- * rolling back the cipher's state to just after the most recent {@code engineInit} completed
- * successfully.
- *
- * <p>Subclasses storing additional post-init state should override this method, reset the
- * additional state, and then chain to superclass.
- */
- @CallSuper
- protected void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
- mMainDataStreamer = null;
- mAdditionalAuthenticationDataStreamer = null;
- mAdditionalAuthenticationDataStreamerClosed = false;
- mCachedException = null;
- }
-
- private void ensureKeystoreOperationInitialized() throws InvalidKeyException,
- InvalidAlgorithmParameterException {
- if (mMainDataStreamer != null) {
- return;
- }
- if (mCachedException != null) {
- return;
- }
- if (mKey == null) {
- throw new IllegalStateException("Not initialized");
- }
-
- KeymasterArguments keymasterInputArgs = new KeymasterArguments();
- addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
- byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, getAdditionalEntropyAmountForBegin());
-
- int purpose;
- if (mKeymasterPurposeOverride != -1) {
- purpose = mKeymasterPurposeOverride;
- } else {
- purpose = mEncrypting
- ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT;
- }
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- purpose,
- true, // permit aborting this operation if keystore runs out of resources
- keymasterInputArgs,
- additionalEntropy,
- mKey.getUid());
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
-
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
-
- // If necessary, throw an exception due to KeyStore operation having failed.
- GeneralSecurityException e = KeyStoreCryptoOperationUtils.getExceptionForCipherInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- if (e instanceof InvalidKeyException) {
- throw (InvalidKeyException) e;
- } else if (e instanceof InvalidAlgorithmParameterException) {
- throw (InvalidAlgorithmParameterException) e;
- } else {
- throw new ProviderException("Unexpected exception type", e);
- }
- }
-
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
- }
-
- loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams);
- mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token);
- mAdditionalAuthenticationDataStreamer =
- createAdditionalAuthenticationDataStreamer(mKeyStore, opResult.token);
- mAdditionalAuthenticationDataStreamerClosed = false;
- }
-
- /**
- * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
- * the corresponding ciphertext/plaintext from the KeyStore.
- *
- * <p>This implementation returns a working streamer.
- */
- @NonNull
- protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
- return new KeyStoreCryptoOperationChunkedStreamer(
- new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken), 0);
- }
-
- /**
- * Creates a streamer which sends Additional Authentication Data (AAD) into the KeyStore.
- *
- * <p>This implementation returns {@code null}.
- *
- * @return stream or {@code null} if AAD is not supported by this cipher.
- */
- @Nullable
- protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
- @SuppressWarnings("unused") KeyStore keyStore,
- @SuppressWarnings("unused") IBinder operationToken) {
- return null;
- }
-
- @Override
- protected final byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
- if (mCachedException != null) {
- return null;
- }
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- mCachedException = e;
- return null;
- }
-
- if (inputLen == 0) {
- return null;
- }
-
- byte[] output;
- try {
- flushAAD();
- output = mMainDataStreamer.update(input, inputOffset, inputLen);
- } catch (KeyStoreException e) {
- mCachedException = e;
- return null;
- }
-
- if (output.length == 0) {
- return null;
- }
-
- return output;
- }
-
- private void flushAAD() throws KeyStoreException {
- if ((mAdditionalAuthenticationDataStreamer != null)
- && (!mAdditionalAuthenticationDataStreamerClosed)) {
- byte[] output;
- try {
- output = mAdditionalAuthenticationDataStreamer.doFinal(
- EmptyArray.BYTE, 0, 0,
- null, // no signature
- null // no additional entropy needed flushing AAD
- );
- } finally {
- mAdditionalAuthenticationDataStreamerClosed = true;
- }
- if ((output != null) && (output.length > 0)) {
- throw new ProviderException(
- "AAD update unexpectedly returned data: " + output.length + " bytes");
- }
- }
- }
-
- @Override
- protected final int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
- int outputOffset) throws ShortBufferException {
- byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
- if (outputCopy == null) {
- return 0;
- }
- int outputAvailable = output.length - outputOffset;
- if (outputCopy.length > outputAvailable) {
- throw new ShortBufferException("Output buffer too short. Produced: "
- + outputCopy.length + ", available: " + outputAvailable);
- }
- System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
- return outputCopy.length;
- }
-
- @Override
- protected final int engineUpdate(ByteBuffer input, ByteBuffer output)
- throws ShortBufferException {
- if (input == null) {
- throw new NullPointerException("input == null");
- }
- if (output == null) {
- throw new NullPointerException("output == null");
- }
-
- int inputSize = input.remaining();
- byte[] outputArray;
- if (input.hasArray()) {
- outputArray =
- engineUpdate(
- input.array(), input.arrayOffset() + input.position(), inputSize);
- input.position(input.position() + inputSize);
- } else {
- byte[] inputArray = new byte[inputSize];
- input.get(inputArray);
- outputArray = engineUpdate(inputArray, 0, inputSize);
- }
-
- int outputSize = (outputArray != null) ? outputArray.length : 0;
- if (outputSize > 0) {
- int outputBufferAvailable = output.remaining();
- try {
- output.put(outputArray);
- } catch (BufferOverflowException e) {
- throw new ShortBufferException(
- "Output buffer too small. Produced: " + outputSize + ", available: "
- + outputBufferAvailable);
- }
- }
- return outputSize;
- }
-
- @Override
- protected final void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) {
- if (mCachedException != null) {
- return;
- }
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- mCachedException = e;
- return;
- }
-
- if (mAdditionalAuthenticationDataStreamerClosed) {
- throw new IllegalStateException(
- "AAD can only be provided before Cipher.update is invoked");
- }
-
- if (mAdditionalAuthenticationDataStreamer == null) {
- throw new IllegalStateException("This cipher does not support AAD");
- }
-
- byte[] output;
- try {
- output = mAdditionalAuthenticationDataStreamer.update(input, inputOffset, inputLen);
- } catch (KeyStoreException e) {
- mCachedException = e;
- return;
- }
-
- if ((output != null) && (output.length > 0)) {
- throw new ProviderException("AAD update unexpectedly produced output: "
- + output.length + " bytes");
- }
- }
-
- @Override
- protected final void engineUpdateAAD(ByteBuffer src) {
- if (src == null) {
- throw new IllegalArgumentException("src == null");
- }
- if (!src.hasRemaining()) {
- return;
- }
-
- byte[] input;
- int inputOffset;
- int inputLen;
- if (src.hasArray()) {
- input = src.array();
- inputOffset = src.arrayOffset() + src.position();
- inputLen = src.remaining();
- src.position(src.limit());
- } else {
- input = new byte[src.remaining()];
- inputOffset = 0;
- inputLen = input.length;
- src.get(input);
- }
- engineUpdateAAD(input, inputOffset, inputLen);
- }
-
- @Override
- protected final byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
- throws IllegalBlockSizeException, BadPaddingException {
- if (mCachedException != null) {
- throw (IllegalBlockSizeException)
- new IllegalBlockSizeException().initCause(mCachedException);
- }
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
- throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
- }
-
- byte[] output;
- try {
- flushAAD();
- byte[] additionalEntropy =
- KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, getAdditionalEntropyAmountForFinish());
- output = mMainDataStreamer.doFinal(
- input, inputOffset, inputLen,
- null, // no signature involved
- additionalEntropy);
- } catch (KeyStoreException e) {
- switch (e.getErrorCode()) {
- case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
- throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
- case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
- throw (BadPaddingException) new BadPaddingException().initCause(e);
- case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
- throw (AEADBadTagException) new AEADBadTagException().initCause(e);
- default:
- throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
- }
- }
-
- resetWhilePreservingInitState();
- return output;
- }
-
- @Override
- protected final int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
- int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
- BadPaddingException {
- byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
- if (outputCopy == null) {
- return 0;
- }
- int outputAvailable = output.length - outputOffset;
- if (outputCopy.length > outputAvailable) {
- throw new ShortBufferException("Output buffer too short. Produced: "
- + outputCopy.length + ", available: " + outputAvailable);
- }
- System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
- return outputCopy.length;
- }
-
- @Override
- protected final int engineDoFinal(ByteBuffer input, ByteBuffer output)
- throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
- if (input == null) {
- throw new NullPointerException("input == null");
- }
- if (output == null) {
- throw new NullPointerException("output == null");
- }
-
- int inputSize = input.remaining();
- byte[] outputArray;
- if (input.hasArray()) {
- outputArray =
- engineDoFinal(
- input.array(), input.arrayOffset() + input.position(), inputSize);
- input.position(input.position() + inputSize);
- } else {
- byte[] inputArray = new byte[inputSize];
- input.get(inputArray);
- outputArray = engineDoFinal(inputArray, 0, inputSize);
- }
-
- int outputSize = (outputArray != null) ? outputArray.length : 0;
- if (outputSize > 0) {
- int outputBufferAvailable = output.remaining();
- try {
- output.put(outputArray);
- } catch (BufferOverflowException e) {
- throw new ShortBufferException(
- "Output buffer too small. Produced: " + outputSize + ", available: "
- + outputBufferAvailable);
- }
- }
- return outputSize;
- }
-
- @Override
- protected final byte[] engineWrap(Key key)
- throws IllegalBlockSizeException, InvalidKeyException {
- if (mKey == null) {
- throw new IllegalStateException("Not initilized");
- }
-
- if (!isEncrypting()) {
- throw new IllegalStateException(
- "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
- }
-
- if (key == null) {
- throw new NullPointerException("key == null");
- }
- byte[] encoded = null;
- if (key instanceof SecretKey) {
- if ("RAW".equalsIgnoreCase(key.getFormat())) {
- encoded = key.getEncoded();
- }
- if (encoded == null) {
- try {
- SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(key.getAlgorithm());
- SecretKeySpec spec =
- (SecretKeySpec) keyFactory.getKeySpec(
- (SecretKey) key, SecretKeySpec.class);
- encoded = spec.getEncoded();
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new InvalidKeyException(
- "Failed to wrap key because it does not export its key material",
- e);
- }
- }
- } else if (key instanceof PrivateKey) {
- if ("PKCS8".equalsIgnoreCase(key.getFormat())) {
- encoded = key.getEncoded();
- }
- if (encoded == null) {
- try {
- KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
- PKCS8EncodedKeySpec spec =
- keyFactory.getKeySpec(key, PKCS8EncodedKeySpec.class);
- encoded = spec.getEncoded();
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new InvalidKeyException(
- "Failed to wrap key because it does not export its key material",
- e);
- }
- }
- } else if (key instanceof PublicKey) {
- if ("X.509".equalsIgnoreCase(key.getFormat())) {
- encoded = key.getEncoded();
- }
- if (encoded == null) {
- try {
- KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
- X509EncodedKeySpec spec =
- keyFactory.getKeySpec(key, X509EncodedKeySpec.class);
- encoded = spec.getEncoded();
- } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
- throw new InvalidKeyException(
- "Failed to wrap key because it does not export its key material",
- e);
- }
- }
- } else {
- throw new InvalidKeyException("Unsupported key type: " + key.getClass().getName());
- }
-
- if (encoded == null) {
- throw new InvalidKeyException(
- "Failed to wrap key because it does not export its key material");
- }
-
- try {
- return engineDoFinal(encoded, 0, encoded.length);
- } catch (BadPaddingException e) {
- throw (IllegalBlockSizeException) new IllegalBlockSizeException().initCause(e);
- }
- }
-
- @Override
- protected final Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
- int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException {
- if (mKey == null) {
- throw new IllegalStateException("Not initilized");
- }
-
- if (isEncrypting()) {
- throw new IllegalStateException(
- "Cipher must be initialized in Cipher.WRAP_MODE to wrap keys");
- }
-
- if (wrappedKey == null) {
- throw new NullPointerException("wrappedKey == null");
- }
-
- byte[] encoded;
- try {
- encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- throw new InvalidKeyException("Failed to unwrap key", e);
- }
-
- switch (wrappedKeyType) {
- case Cipher.SECRET_KEY:
- {
- return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
- // break;
- }
- case Cipher.PRIVATE_KEY:
- {
- KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
- try {
- return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
- } catch (InvalidKeySpecException e) {
- throw new InvalidKeyException(
- "Failed to create private key from its PKCS#8 encoded form", e);
- }
- // break;
- }
- case Cipher.PUBLIC_KEY:
- {
- KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
- try {
- return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
- } catch (InvalidKeySpecException e) {
- throw new InvalidKeyException(
- "Failed to create public key from its X.509 encoded form", e);
- }
- // break;
- }
- default:
- throw new InvalidParameterException(
- "Unsupported wrappedKeyType: " + wrappedKeyType);
- }
- }
-
- @Override
- protected final void engineSetMode(String mode) throws NoSuchAlgorithmException {
- // This should never be invoked because all algorithms registered with the AndroidKeyStore
- // provide explicitly specify block mode.
- throw new UnsupportedOperationException();
- }
-
- @Override
- protected final void engineSetPadding(String arg0) throws NoSuchPaddingException {
- // This should never be invoked because all algorithms registered with the AndroidKeyStore
- // provide explicitly specify padding mode.
- throw new UnsupportedOperationException();
- }
-
- @Override
- protected final int engineGetKeySize(Key key) throws InvalidKeyException {
- throw new UnsupportedOperationException();
- }
-
- @CallSuper
- @Override
- public void finalize() throws Throwable {
- try {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- } finally {
- super.finalize();
- }
- }
-
- @Override
- public final long getOperationHandle() {
- return mOperationHandle;
- }
-
- protected final void setKey(@NonNull AndroidKeyStoreKey key) {
- mKey = key;
- }
-
- /**
- * Overrides the default purpose/type of the crypto operation.
- */
- protected final void setKeymasterPurposeOverride(int keymasterPurpose) {
- mKeymasterPurposeOverride = keymasterPurpose;
- }
-
- protected final int getKeymasterPurposeOverride() {
- return mKeymasterPurposeOverride;
- }
-
- /**
- * Returns {@code true} if this cipher is initialized for encryption, {@code false} if this
- * cipher is initialized for decryption.
- */
- protected final boolean isEncrypting() {
- return mEncrypting;
- }
-
- @NonNull
- protected final KeyStore getKeyStore() {
- return mKeyStore;
- }
-
- protected final long getConsumedInputSizeBytes() {
- if (mMainDataStreamer == null) {
- throw new IllegalStateException("Not initialized");
- }
- return mMainDataStreamer.getConsumedInputSizeBytes();
- }
-
- protected final long getProducedOutputSizeBytes() {
- if (mMainDataStreamer == null) {
- throw new IllegalStateException("Not initialized");
- }
- return mMainDataStreamer.getProducedOutputSizeBytes();
- }
-
- static String opmodeToString(int opmode) {
- switch (opmode) {
- case Cipher.ENCRYPT_MODE:
- return "ENCRYPT_MODE";
- case Cipher.DECRYPT_MODE:
- return "DECRYPT_MODE";
- case Cipher.WRAP_MODE:
- return "WRAP_MODE";
- case Cipher.UNWRAP_MODE:
- return "UNWRAP_MODE";
- default:
- return String.valueOf(opmode);
- }
- }
-
- // The methods below need to be implemented by subclasses.
-
- /**
- * Initializes this cipher with the provided key.
- *
- * @throws InvalidKeyException if the {@code key} is not suitable for this cipher in the
- * specified {@code opmode}.
- *
- * @see #setKey(AndroidKeyStoreKey)
- */
- protected abstract void initKey(int opmode, @Nullable Key key) throws InvalidKeyException;
-
- /**
- * Returns algorithm-specific parameters used by this cipher or {@code null} if no
- * algorithm-specific parameters are used.
- */
- @Nullable
- @Override
- protected abstract AlgorithmParameters engineGetParameters();
-
- /**
- * Invoked by {@code engineInit} to initialize algorithm-specific parameters when no additional
- * initialization parameters were provided.
- *
- * @throws InvalidKeyException if this cipher cannot be configured based purely on the provided
- * key and needs additional parameters to be provided to {@code Cipher.init}.
- */
- protected abstract void initAlgorithmSpecificParameters() throws InvalidKeyException;
-
- /**
- * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
- * parameters were provided.
- *
- * @param params additional algorithm parameters or {@code null} if not specified.
- *
- * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
- * this cipher or if the provided parameters are not suitable for this cipher.
- */
- protected abstract void initAlgorithmSpecificParameters(
- @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException;
-
- /**
- * Invoked by {@code engineInit} to initialize algorithm-specific parameters when additional
- * parameters were provided.
- *
- * @param params additional algorithm parameters or {@code null} if not specified.
- *
- * @throws InvalidAlgorithmParameterException if there is insufficient information to configure
- * this cipher or if the provided parameters are not suitable for this cipher.
- */
- protected abstract void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
- throws InvalidAlgorithmParameterException;
-
- /**
- * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
- * {@code begin} operation. This amount of entropy is typically what's consumed to generate
- * random parameters, such as IV.
- *
- * <p>For decryption, the return value should be {@code 0} because decryption should not be
- * consuming any entropy. For encryption, the value combined with
- * {@link #getAdditionalEntropyAmountForFinish()} should match (or exceed) the amount of Shannon
- * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
- * explicitly provided parameters to {@code Cipher.init} are known. For example, for AES CBC
- * encryption with an explicitly provided IV the return value should be {@code 0}, whereas for
- * the case where IV is generated by the KeyStore's {@code begin} operation it should be
- * {@code 16}.
- */
- protected abstract int getAdditionalEntropyAmountForBegin();
-
- /**
- * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
- * {@code finish} operation. This amount of entropy is typically what's consumed by encryption
- * padding scheme.
- *
- * <p>For decryption, the return value should be {@code 0} because decryption should not be
- * consuming any entropy. For encryption, the value combined with
- * {@link #getAdditionalEntropyAmountForBegin()} should match (or exceed) the amount of Shannon
- * entropy of the ciphertext produced by this cipher assuming the key, the plaintext, and all
- * explicitly provided parameters to {@code Cipher.init} are known. For example, for RSA with
- * OAEP the return value should be the size of the OAEP hash output. For RSA with PKCS#1 padding
- * the return value should be the size of the padding string or could be raised (for simplicity)
- * to the size of the modulus.
- */
- protected abstract int getAdditionalEntropyAmountForFinish();
-
- /**
- * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
- *
- * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
- * parameters.
- */
- protected abstract void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs);
-
- /**
- * Invoked to obtain algorithm-specific parameters from the result of the KeyStore's
- * {@code begin} operation.
- *
- * <p>Some parameters, such as IV, are not required to be provided to {@code Cipher.init}. Such
- * parameters, if not provided, must be generated by KeyStore and returned to the user of
- * {@code Cipher} and potentially reused after {@code doFinal}.
- *
- * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin}
- * operation.
- */
- protected abstract void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs);
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
deleted file mode 100644
index 45f2110..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECDSASignatureSpi.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import libcore.util.EmptyArray;
-
-import java.io.ByteArrayOutputStream;
-import java.security.InvalidKeyException;
-import java.security.SignatureSpi;
-
-/**
- * Base class for {@link SignatureSpi} providing Android KeyStore backed ECDSA signatures.
- *
- * @hide
- */
-abstract class AndroidKeyStoreECDSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
-
- public final static class NONE extends AndroidKeyStoreECDSASignatureSpi {
- public NONE() {
- super(KeymasterDefs.KM_DIGEST_NONE);
- }
-
- @Override
- protected KeyStoreCryptoOperationStreamer createMainDataStreamer(KeyStore keyStore,
- IBinder operationToken) {
- return new TruncateToFieldSizeMessageStreamer(
- super.createMainDataStreamer(keyStore, operationToken),
- getGroupSizeBits());
- }
-
- /**
- * Streamer which buffers all input, then truncates it to field size, and then sends it into
- * KeyStore via the provided delegate streamer.
- */
- private static class TruncateToFieldSizeMessageStreamer
- implements KeyStoreCryptoOperationStreamer {
-
- private final KeyStoreCryptoOperationStreamer mDelegate;
- private final int mGroupSizeBits;
- private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
- private long mConsumedInputSizeBytes;
-
- private TruncateToFieldSizeMessageStreamer(
- KeyStoreCryptoOperationStreamer delegate,
- int groupSizeBits) {
- mDelegate = delegate;
- mGroupSizeBits = groupSizeBits;
- }
-
- @Override
- public byte[] update(byte[] input, int inputOffset, int inputLength)
- throws KeyStoreException {
- if (inputLength > 0) {
- mInputBuffer.write(input, inputOffset, inputLength);
- mConsumedInputSizeBytes += inputLength;
- }
- return EmptyArray.BYTE;
- }
-
- @Override
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
- byte[] additionalEntropy) throws KeyStoreException {
- if (inputLength > 0) {
- mConsumedInputSizeBytes += inputLength;
- mInputBuffer.write(input, inputOffset, inputLength);
- }
-
- byte[] bufferedInput = mInputBuffer.toByteArray();
- mInputBuffer.reset();
- // Truncate input at field size (bytes)
- return mDelegate.doFinal(bufferedInput,
- 0,
- Math.min(bufferedInput.length, ((mGroupSizeBits + 7) / 8)),
- signature, additionalEntropy);
- }
-
- @Override
- public long getConsumedInputSizeBytes() {
- return mConsumedInputSizeBytes;
- }
-
- @Override
- public long getProducedOutputSizeBytes() {
- return mDelegate.getProducedOutputSizeBytes();
- }
- }
- }
-
- public final static class SHA1 extends AndroidKeyStoreECDSASignatureSpi {
- public SHA1() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public final static class SHA224 extends AndroidKeyStoreECDSASignatureSpi {
- public SHA224() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public final static class SHA256 extends AndroidKeyStoreECDSASignatureSpi {
- public SHA256() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public final static class SHA384 extends AndroidKeyStoreECDSASignatureSpi {
- public SHA384() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public final static class SHA512 extends AndroidKeyStoreECDSASignatureSpi {
- public SHA512() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- private final int mKeymasterDigest;
-
- private int mGroupSizeBits = -1;
-
- AndroidKeyStoreECDSASignatureSpi(int keymasterDigest) {
- mKeymasterDigest = keymasterDigest;
- }
-
- @Override
- protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
- if (!KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
- + ". Only" + KeyProperties.KEY_ALGORITHM_EC + " supported");
- }
-
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = getKeyStore().getKeyCharacteristics(
- key.getAlias(), null, null, key.getUid(), keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(key.getAlias(), key.getUid(), errorCode);
- }
- long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
- if (keySizeBits == -1) {
- throw new InvalidKeyException("Size of key not known");
- } else if (keySizeBits > Integer.MAX_VALUE) {
- throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
- }
- mGroupSizeBits = (int) keySizeBits;
-
- super.initKey(key);
- }
-
- @Override
- protected final void resetAll() {
- mGroupSizeBits = -1;
- super.resetAll();
- }
-
- @Override
- protected final void resetWhilePreservingInitState() {
- super.resetWhilePreservingInitState();
- }
-
- @Override
- protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_EC);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForSign() {
- return (mGroupSizeBits + 7) / 8;
- }
-
- protected final int getGroupSizeBits() {
- if (mGroupSizeBits == -1) {
- throw new IllegalStateException("Not initialized");
- }
- return mGroupSizeBits;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java
deleted file mode 100644
index aa7bdff..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECPrivateKey.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.PrivateKey;
-import java.security.interfaces.ECKey;
-import java.security.spec.ECParameterSpec;
-
-/**
- * EC private key (instance of {@link PrivateKey} and {@link ECKey}) backed by keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreECPrivateKey extends AndroidKeyStorePrivateKey implements ECKey {
- private final ECParameterSpec mParams;
-
- public AndroidKeyStoreECPrivateKey(String alias, int uid, ECParameterSpec params) {
- super(alias, uid, KeyProperties.KEY_ALGORITHM_EC);
- mParams = params;
- }
-
- @Override
- public ECParameterSpec getParams() {
- return mParams;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
deleted file mode 100644
index 2efaeb6..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreECPublicKey.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-
-/**
- * {@link ECPublicKey} backed by keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreECPublicKey extends AndroidKeyStorePublicKey implements ECPublicKey {
-
- private final ECParameterSpec mParams;
- private final ECPoint mW;
-
- public AndroidKeyStoreECPublicKey(String alias, int uid, byte[] x509EncodedForm, ECParameterSpec params,
- ECPoint w) {
- super(alias, uid, KeyProperties.KEY_ALGORITHM_EC, x509EncodedForm);
- mParams = params;
- mW = w;
- }
-
- public AndroidKeyStoreECPublicKey(String alias, int uid, ECPublicKey info) {
- this(alias, uid, info.getEncoded(), info.getParams(), info.getW());
- if (!"X.509".equalsIgnoreCase(info.getFormat())) {
- throw new IllegalArgumentException(
- "Unsupported key export format: " + info.getFormat());
- }
- }
-
- @Override
- public ECParameterSpec getParams() {
- return mParams;
- }
-
- @Override
- public ECPoint getW() {
- return mW;
- }
-}
\ No newline at end of file
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
deleted file mode 100644
index 2e8ac32..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreHmacSpi.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.ProviderException;
-import java.security.spec.AlgorithmParameterSpec;
-
-import javax.crypto.MacSpi;
-
-/**
- * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
- *
- * @hide
- */
-public abstract class AndroidKeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
-
- public static class HmacSHA1 extends AndroidKeyStoreHmacSpi {
- public HmacSHA1() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public static class HmacSHA224 extends AndroidKeyStoreHmacSpi {
- public HmacSHA224() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public static class HmacSHA256 extends AndroidKeyStoreHmacSpi {
- public HmacSHA256() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public static class HmacSHA384 extends AndroidKeyStoreHmacSpi {
- public HmacSHA384() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public static class HmacSHA512 extends AndroidKeyStoreHmacSpi {
- public HmacSHA512() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- private final KeyStore mKeyStore = KeyStore.getInstance();
- private final int mKeymasterDigest;
- private final int mMacSizeBits;
-
- // Fields below are populated by engineInit and should be preserved after engineDoFinal.
- private AndroidKeyStoreSecretKey mKey;
-
- // Fields below are reset when engineDoFinal succeeds.
- private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
- private IBinder mOperationToken;
- private long mOperationHandle;
-
- protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
- mKeymasterDigest = keymasterDigest;
- mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
- }
-
- @Override
- protected int engineGetMacLength() {
- return (mMacSizeBits + 7) / 8;
- }
-
- @Override
- protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
- InvalidAlgorithmParameterException {
- resetAll();
-
- boolean success = false;
- try {
- init(key, params);
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- private void init(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
- InvalidAlgorithmParameterException {
- if (key == null) {
- throw new InvalidKeyException("key == null");
- } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeyException(
- "Only Android KeyStore secret keys supported. Key: " + key);
- }
- mKey = (AndroidKeyStoreSecretKey) key;
-
- if (params != null) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported algorithm parameters: " + params);
- }
-
- }
-
- private void resetAll() {
- mKey = null;
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
- mChunkedStreamer = null;
- }
-
- private void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- mOperationToken = null;
- mOperationHandle = 0;
- mChunkedStreamer = null;
- }
-
- @Override
- protected void engineReset() {
- resetWhilePreservingInitState();
- }
-
- private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
- if (mChunkedStreamer != null) {
- return;
- }
- if (mKey == null) {
- throw new IllegalStateException("Not initialized");
- }
-
- KeymasterArguments keymasterArgs = new KeymasterArguments();
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_HMAC);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mMacSizeBits);
-
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- KeymasterDefs.KM_PURPOSE_SIGN,
- true,
- keymasterArgs,
- null, // no additional entropy needed for HMAC because it's deterministic
- mKey.getUid());
-
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
-
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
-
- // If necessary, throw an exception due to KeyStore operation having failed.
- InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- throw e;
- }
-
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
- }
-
- mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
- new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- mKeyStore, mOperationToken));
- }
-
- @Override
- protected void engineUpdate(byte input) {
- engineUpdate(new byte[] {input}, 0, 1);
- }
-
- @Override
- protected void engineUpdate(byte[] input, int offset, int len) {
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new ProviderException("Failed to reinitialize MAC", e);
- }
-
- byte[] output;
- try {
- output = mChunkedStreamer.update(input, offset, len);
- } catch (KeyStoreException e) {
- throw new ProviderException("Keystore operation failed", e);
- }
- if ((output != null) && (output.length != 0)) {
- throw new ProviderException("Update operation unexpectedly produced output");
- }
- }
-
- @Override
- protected byte[] engineDoFinal() {
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new ProviderException("Failed to reinitialize MAC", e);
- }
-
- byte[] result;
- try {
- result = mChunkedStreamer.doFinal(
- null, 0, 0,
- null, // no signature provided -- this invocation will generate one
- null // no additional entropy needed -- HMAC is deterministic
- );
- } catch (KeyStoreException e) {
- throw new ProviderException("Keystore operation failed", e);
- }
-
- resetWhilePreservingInitState();
- return result;
- }
-
- @Override
- public void finalize() throws Throwable {
- try {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mKeyStore.abort(operationToken);
- }
- } finally {
- super.finalize();
- }
- }
-
- @Override
- public long getOperationHandle() {
- return mOperationHandle;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
deleted file mode 100644
index e8e6310..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.Key;
-
-/**
- * {@link Key} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreKey implements Key {
- private final String mAlias;
- private final int mUid;
- private final String mAlgorithm;
-
- public AndroidKeyStoreKey(String alias, int uid, String algorithm) {
- mAlias = alias;
- mUid = uid;
- mAlgorithm = algorithm;
- }
-
- String getAlias() {
- return mAlias;
- }
-
- int getUid() {
- return mUid;
- }
-
- @Override
- public String getAlgorithm() {
- return mAlgorithm;
- }
-
- @Override
- public String getFormat() {
- // This key does not export its key material
- return null;
- }
-
- @Override
- public byte[] getEncoded() {
- // This key does not export its key material
- return null;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((mAlgorithm == null) ? 0 : mAlgorithm.hashCode());
- result = prime * result + ((mAlias == null) ? 0 : mAlias.hashCode());
- result = prime * result + mUid;
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- AndroidKeyStoreKey other = (AndroidKeyStoreKey) obj;
- if (mAlgorithm == null) {
- if (other.mAlgorithm != null) {
- return false;
- }
- } else if (!mAlgorithm.equals(other.mAlgorithm)) {
- return false;
- }
- if (mAlias == null) {
- if (other.mAlias != null) {
- return false;
- }
- } else if (!mAlias.equals(other.mAlias)) {
- return false;
- }
- if (mUid != other.mUid) {
- return false;
- }
- return true;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
deleted file mode 100644
index 303b0f2..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyFactorySpi.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.Credentials;
-import android.security.KeyStore;
-
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.KeyFactorySpi;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-
-/**
- * {@link KeyFactorySpi} backed by Android KeyStore.
- *
- * @hide
- */
-public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
-
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
- @Override
- protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
- throws InvalidKeySpecException {
- if (key == null) {
- throw new InvalidKeySpecException("key == null");
- } else if ((!(key instanceof AndroidKeyStorePrivateKey))
- && (!(key instanceof AndroidKeyStorePublicKey))) {
- throw new InvalidKeySpecException(
- "Unsupported key type: " + key.getClass().getName()
- + ". This KeyFactory supports only Android Keystore asymmetric keys");
- }
-
- // key is an Android Keystore private or public key
-
- if (keySpecClass == null) {
- throw new InvalidKeySpecException("keySpecClass == null");
- } else if (KeyInfo.class.equals(keySpecClass)) {
- if (!(key instanceof AndroidKeyStorePrivateKey)) {
- throw new InvalidKeySpecException(
- "Unsupported key type: " + key.getClass().getName()
- + ". KeyInfo can be obtained only for Android Keystore private keys");
- }
- AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
- String keyAliasInKeystore = keystorePrivateKey.getAlias();
- String entryAlias;
- if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
- entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
- } else {
- throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
- }
- @SuppressWarnings("unchecked")
- T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
- mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid());
- return result;
- } else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
- if (!(key instanceof AndroidKeyStorePublicKey)) {
- throw new InvalidKeySpecException(
- "Unsupported key type: " + key.getClass().getName()
- + ". X509EncodedKeySpec can be obtained only for Android Keystore public"
- + " keys");
- }
- @SuppressWarnings("unchecked")
- T result = (T) new X509EncodedKeySpec(((AndroidKeyStorePublicKey) key).getEncoded());
- return result;
- } else if (PKCS8EncodedKeySpec.class.equals(keySpecClass)) {
- if (key instanceof AndroidKeyStorePrivateKey) {
- throw new InvalidKeySpecException(
- "Key material export of Android Keystore private keys is not supported");
- } else {
- throw new InvalidKeySpecException(
- "Cannot export key material of public key in PKCS#8 format."
- + " Only X.509 format (X509EncodedKeySpec) supported for public keys.");
- }
- } else if (RSAPublicKeySpec.class.equals(keySpecClass)) {
- if (key instanceof AndroidKeyStoreRSAPublicKey) {
- AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key;
- @SuppressWarnings("unchecked")
- T result =
- (T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
- return result;
- } else {
- throw new InvalidKeySpecException(
- "Obtaining RSAPublicKeySpec not supported for " + key.getAlgorithm() + " "
- + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
- + " key");
- }
- } else if (ECPublicKeySpec.class.equals(keySpecClass)) {
- if (key instanceof AndroidKeyStoreECPublicKey) {
- AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key;
- @SuppressWarnings("unchecked")
- T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
- return result;
- } else {
- throw new InvalidKeySpecException(
- "Obtaining ECPublicKeySpec not supported for " + key.getAlgorithm() + " "
- + ((key instanceof AndroidKeyStorePrivateKey) ? "private" : "public")
- + " key");
- }
- } else {
- throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
- }
- }
-
- @Override
- protected PrivateKey engineGeneratePrivate(KeySpec spec) throws InvalidKeySpecException {
- throw new InvalidKeySpecException(
- "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
- + " " + KeyGenParameterSpec.class.getName());
- }
-
- @Override
- protected PublicKey engineGeneratePublic(KeySpec spec) throws InvalidKeySpecException {
- throw new InvalidKeySpecException(
- "To generate a key pair in Android Keystore, use KeyPairGenerator initialized with"
- + " " + KeyGenParameterSpec.class.getName());
- }
-
- @Override
- protected Key engineTranslateKey(Key key) throws InvalidKeyException {
- if (key == null) {
- throw new InvalidKeyException("key == null");
- } else if ((!(key instanceof AndroidKeyStorePrivateKey))
- && (!(key instanceof AndroidKeyStorePublicKey))) {
- throw new InvalidKeyException(
- "To import a key into Android Keystore, use KeyStore.setEntry");
- }
- return key;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
deleted file mode 100644
index fedde42..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-
-import libcore.util.EmptyArray;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.ProviderException;
-import java.security.SecureRandom;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.Arrays;
-
-import javax.crypto.KeyGeneratorSpi;
-import javax.crypto.SecretKey;
-
-/**
- * {@link KeyGeneratorSpi} backed by Android KeyStore.
- *
- * @hide
- */
-public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
-
- public static class AES extends AndroidKeyStoreKeyGeneratorSpi {
- public AES() {
- super(KeymasterDefs.KM_ALGORITHM_AES, 128);
- }
-
- @Override
- protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- super.engineInit(params, random);
- if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported key size: " + mKeySizeBits
- + ". Supported: 128, 192, 256.");
- }
- }
- }
-
- public static class DESede extends AndroidKeyStoreKeyGeneratorSpi {
- public DESede() {
- super(KeymasterDefs.KM_ALGORITHM_3DES, 168);
- }
- }
-
- protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi {
- protected HmacBase(int keymasterDigest) {
- super(KeymasterDefs.KM_ALGORITHM_HMAC,
- keymasterDigest,
- KeymasterUtils.getDigestOutputSizeBits(keymasterDigest));
- }
- }
-
- public static class HmacSHA1 extends HmacBase {
- public HmacSHA1() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public static class HmacSHA224 extends HmacBase {
- public HmacSHA224() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public static class HmacSHA256 extends HmacBase {
- public HmacSHA256() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public static class HmacSHA384 extends HmacBase {
- public HmacSHA384() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public static class HmacSHA512 extends HmacBase {
- public HmacSHA512() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- private final KeyStore mKeyStore = KeyStore.getInstance();
- private final int mKeymasterAlgorithm;
- private final int mKeymasterDigest;
- private final int mDefaultKeySizeBits;
-
- private KeyGenParameterSpec mSpec;
- private SecureRandom mRng;
-
- protected int mKeySizeBits;
- private int[] mKeymasterPurposes;
- private int[] mKeymasterBlockModes;
- private int[] mKeymasterPaddings;
- private int[] mKeymasterDigests;
-
- protected AndroidKeyStoreKeyGeneratorSpi(
- int keymasterAlgorithm,
- int defaultKeySizeBits) {
- this(keymasterAlgorithm, -1, defaultKeySizeBits);
- }
-
- protected AndroidKeyStoreKeyGeneratorSpi(
- int keymasterAlgorithm,
- int keymasterDigest,
- int defaultKeySizeBits) {
- mKeymasterAlgorithm = keymasterAlgorithm;
- mKeymasterDigest = keymasterDigest;
- mDefaultKeySizeBits = defaultKeySizeBits;
- if (mDefaultKeySizeBits <= 0) {
- throw new IllegalArgumentException("Default key size must be positive");
- }
-
- if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
- throw new IllegalArgumentException(
- "Digest algorithm must be specified for HMAC key");
- }
- }
-
- @Override
- protected void engineInit(SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without a "
- + KeyGenParameterSpec.class.getName() + " parameter");
- }
-
- @Override
- protected void engineInit(int keySize, SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without a "
- + KeyGenParameterSpec.class.getName() + " parameter");
- }
-
- @Override
- protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- resetAll();
-
- boolean success = false;
- try {
- if ((params == null) || (!(params instanceof KeyGenParameterSpec))) {
- throw new InvalidAlgorithmParameterException("Cannot initialize without a "
- + KeyGenParameterSpec.class.getName() + " parameter");
- }
- KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
- if (spec.getKeystoreAlias() == null) {
- throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
- }
-
- mRng = random;
- mSpec = spec;
-
- mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
- if (mKeySizeBits <= 0) {
- throw new InvalidAlgorithmParameterException(
- "Key size must be positive: " + mKeySizeBits);
- } else if ((mKeySizeBits % 8) != 0) {
- throw new InvalidAlgorithmParameterException(
- "Key size must be a multiple of 8: " + mKeySizeBits);
- }
-
- try {
- mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
- mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
- spec.getEncryptionPaddings());
- if (spec.getSignaturePaddings().length > 0) {
- throw new InvalidAlgorithmParameterException(
- "Signature paddings not supported for symmetric key algorithms");
- }
- mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
- if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (spec.isRandomizedEncryptionRequired())) {
- for (int keymasterBlockMode : mKeymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
- keymasterBlockMode)) {
- throw new InvalidAlgorithmParameterException(
- "Randomized encryption (IND-CPA) required but may be violated"
- + " by block mode: "
- + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
- + ". See " + KeyGenParameterSpec.class.getName()
- + " documentation.");
- }
- }
- }
- if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
- if (mKeySizeBits != 168) {
- throw new InvalidAlgorithmParameterException(
- "3DES key size must be 168 bits.");
- }
- }
- if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
- if (mKeySizeBits < 64 || mKeySizeBits > 512) {
- throw new InvalidAlgorithmParameterException(
- "HMAC key sizes must be within 64-512 bits, inclusive.");
- }
-
- // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
- // implies SHA-256 digest). Because keymaster HMAC key is authorized only for
- // one digest, we don't let algorithm parameter spec override the digest implied
- // by the key. If the spec specifies digests at all, it must specify only one
- // digest, the only implied by key algorithm.
- mKeymasterDigests = new int[] {mKeymasterDigest};
- if (spec.isDigestsSpecified()) {
- // Digest(s) explicitly specified in the spec. Check that the list
- // consists of exactly one digest, the one implied by key algorithm.
- int[] keymasterDigestsFromSpec =
- KeyProperties.Digest.allToKeymaster(spec.getDigests());
- if ((keymasterDigestsFromSpec.length != 1)
- || (keymasterDigestsFromSpec[0] != mKeymasterDigest)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported digests specification: "
- + Arrays.asList(spec.getDigests()) + ". Only "
- + KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
- + " supported for this HMAC key algorithm");
- }
- }
- } else {
- // Key algorithm does not imply a digest.
- if (spec.isDigestsSpecified()) {
- mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
- } else {
- mKeymasterDigests = EmptyArray.INT;
- }
- }
-
- // Check that user authentication related parameters are acceptable. This method
- // will throw an IllegalStateException if there are issues (e.g., secure lock screen
- // not set up).
- KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
- } catch (IllegalStateException | IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(e);
- }
-
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- private void resetAll() {
- mSpec = null;
- mRng = null;
- mKeySizeBits = -1;
- mKeymasterPurposes = null;
- mKeymasterPaddings = null;
- mKeymasterBlockModes = null;
- }
-
- @Override
- protected SecretKey engineGenerateKey() {
- KeyGenParameterSpec spec = mSpec;
- if (spec == null) {
- throw new IllegalStateException("Not initialized");
- }
-
- KeymasterArguments args = new KeymasterArguments();
- args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
- args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
- KeymasterUtils.addUserAuthArgs(args, spec);
- KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
- args,
- mKeymasterAlgorithm,
- mKeymasterBlockModes,
- mKeymasterDigests);
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, spec.getKeyValidityStart());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- spec.getKeyValidityForOriginationEnd());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- spec.getKeyValidityForConsumptionEnd());
-
- if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (!spec.isRandomizedEncryptionRequired())) {
- // Permit caller-provided IV when encrypting with this key
- args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
- }
-
- byte[] additionalEntropy =
- KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, (mKeySizeBits + 7) / 8);
- int flags = 0;
- if (spec.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
- }
- if (spec.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
- }
- String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
- KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
- boolean success = false;
- try {
- Credentials.deleteAllTypesForAlias(mKeyStore, spec.getKeystoreAlias(), spec.getUid());
- int errorCode = mKeyStore.generateKey(
- keyAliasInKeystore,
- args,
- additionalEntropy,
- spec.getUid(),
- flags,
- resultingKeyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) {
- throw new StrongBoxUnavailableException("Failed to generate key");
- } else {
- throw new ProviderException(
- "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
- }
- }
- @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
- try {
- keyAlgorithmJCA = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
- mKeymasterAlgorithm, mKeymasterDigest);
- } catch (IllegalArgumentException e) {
- throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
- }
- SecretKey result = new AndroidKeyStoreSecretKey(
- keyAliasInKeystore, spec.getUid(), keyAlgorithmJCA);
- success = true;
- return result;
- } finally {
- if (!success) {
- Credentials.deleteAllTypesForAlias(
- mKeyStore, spec.getKeystoreAlias(), spec.getUid());
- }
- }
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
deleted file mode 100644
index 988838b..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ /dev/null
@@ -1,986 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.Build;
-import android.security.Credentials;
-import android.security.KeyPairGeneratorSpec;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keymaster.KeymasterDefs;
-import android.telephony.TelephonyManager;
-import android.util.ArraySet;
-
-import com.android.internal.org.bouncycastle.asn1.ASN1EncodableVector;
-import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
-import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import com.android.internal.org.bouncycastle.asn1.DERBitString;
-import com.android.internal.org.bouncycastle.asn1.DERNull;
-import com.android.internal.org.bouncycastle.asn1.DERSequence;
-import com.android.internal.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import com.android.internal.org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
-import com.android.internal.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import com.android.internal.org.bouncycastle.asn1.x509.TBSCertificate;
-import com.android.internal.org.bouncycastle.asn1.x509.Time;
-import com.android.internal.org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
-import com.android.internal.org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import com.android.internal.org.bouncycastle.jce.X509Principal;
-import com.android.internal.org.bouncycastle.jce.provider.X509CertificateObject;
-import com.android.internal.org.bouncycastle.x509.X509V3CertificateGenerator;
-
-import libcore.util.EmptyArray;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyPairGeneratorSpi;
-import java.security.PrivateKey;
-import java.security.ProviderException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.ECGenParameterSpec;
-import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Provides a way to create instances of a KeyPair which will be placed in the
- * Android keystore service usable only by the application that called it. This
- * can be used in conjunction with
- * {@link java.security.KeyStore#getInstance(String)} using the
- * {@code "AndroidKeyStore"} type.
- * <p>
- * This class can not be directly instantiated and must instead be used via the
- * {@link KeyPairGenerator#getInstance(String)
- * KeyPairGenerator.getInstance("AndroidKeyStore")} API.
- *
- * @hide
- */
-public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGeneratorSpi {
-
- public static class RSA extends AndroidKeyStoreKeyPairGeneratorSpi {
- public RSA() {
- super(KeymasterDefs.KM_ALGORITHM_RSA);
- }
- }
-
- public static class EC extends AndroidKeyStoreKeyPairGeneratorSpi {
- public EC() {
- super(KeymasterDefs.KM_ALGORITHM_EC);
- }
- }
-
- /*
- * These must be kept in sync with system/security/keystore/defaults.h
- */
-
- /* EC */
- private static final int EC_DEFAULT_KEY_SIZE = 256;
-
- /* RSA */
- private static final int RSA_DEFAULT_KEY_SIZE = 2048;
- private static final int RSA_MIN_KEY_SIZE = 512;
- private static final int RSA_MAX_KEY_SIZE = 8192;
-
- private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE =
- new HashMap<String, Integer>();
- private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
- private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>();
- static {
- // Aliases for NIST P-224
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
-
-
- // Aliases for NIST P-256
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
-
- // Aliases for NIST P-384
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
-
- // Aliases for NIST P-521
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521);
- SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
-
- SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
- Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
-
- SUPPORTED_EC_NIST_CURVE_SIZES.addAll(
- new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values()));
- Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES);
- }
-
- private final int mOriginalKeymasterAlgorithm;
-
- private KeyStore mKeyStore;
-
- private KeyGenParameterSpec mSpec;
-
- private String mEntryAlias;
- private int mEntryUid;
- private boolean mEncryptionAtRestRequired;
- private @KeyProperties.KeyAlgorithmEnum String mJcaKeyAlgorithm;
- private int mKeymasterAlgorithm = -1;
- private int mKeySizeBits;
- private SecureRandom mRng;
-
- private int[] mKeymasterPurposes;
- private int[] mKeymasterBlockModes;
- private int[] mKeymasterEncryptionPaddings;
- private int[] mKeymasterSignaturePaddings;
- private int[] mKeymasterDigests;
-
- private BigInteger mRSAPublicExponent;
-
- protected AndroidKeyStoreKeyPairGeneratorSpi(int keymasterAlgorithm) {
- mOriginalKeymasterAlgorithm = keymasterAlgorithm;
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void initialize(int keysize, SecureRandom random) {
- throw new IllegalArgumentException(
- KeyGenParameterSpec.class.getName() + " or " + KeyPairGeneratorSpec.class.getName()
- + " required to initialize this KeyPairGenerator");
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public void initialize(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- resetAll();
-
- boolean success = false;
- try {
- if (params == null) {
- throw new InvalidAlgorithmParameterException(
- "Must supply params of type " + KeyGenParameterSpec.class.getName()
- + " or " + KeyPairGeneratorSpec.class.getName());
- }
-
- KeyGenParameterSpec spec;
- boolean encryptionAtRestRequired = false;
- int keymasterAlgorithm = mOriginalKeymasterAlgorithm;
- if (params instanceof KeyGenParameterSpec) {
- spec = (KeyGenParameterSpec) params;
- } else if (params instanceof KeyPairGeneratorSpec) {
- // Legacy/deprecated spec
- KeyPairGeneratorSpec legacySpec = (KeyPairGeneratorSpec) params;
- try {
- KeyGenParameterSpec.Builder specBuilder;
- String specKeyAlgorithm = legacySpec.getKeyType();
- if (specKeyAlgorithm != null) {
- // Spec overrides the generator's default key algorithm
- try {
- keymasterAlgorithm =
- KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
- specKeyAlgorithm);
- } catch (IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(
- "Invalid key type in parameters", e);
- }
- }
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- // MD5 was never offered for Android Keystore for ECDSA.
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- break;
- case KeymasterDefs.KM_ALGORITHM_RSA:
- specBuilder = new KeyGenParameterSpec.Builder(
- legacySpec.getKeystoreAlias(),
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT
- | KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- // Authorized to be used with any encryption and signature padding
- // schemes (including no padding).
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
- KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
- KeyProperties.SIGNATURE_PADDING_RSA_PSS);
- // Disable randomized encryption requirement to support encryption
- // padding NONE above.
- specBuilder.setRandomizedEncryptionRequired(false);
- break;
- default:
- throw new ProviderException(
- "Unsupported algorithm: " + mKeymasterAlgorithm);
- }
-
- if (legacySpec.getKeySize() != -1) {
- specBuilder.setKeySize(legacySpec.getKeySize());
- }
- if (legacySpec.getAlgorithmParameterSpec() != null) {
- specBuilder.setAlgorithmParameterSpec(
- legacySpec.getAlgorithmParameterSpec());
- }
- specBuilder.setCertificateSubject(legacySpec.getSubjectDN());
- specBuilder.setCertificateSerialNumber(legacySpec.getSerialNumber());
- specBuilder.setCertificateNotBefore(legacySpec.getStartDate());
- specBuilder.setCertificateNotAfter(legacySpec.getEndDate());
- encryptionAtRestRequired = legacySpec.isEncryptionRequired();
- specBuilder.setUserAuthenticationRequired(false);
-
- spec = specBuilder.build();
- } catch (NullPointerException | IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(e);
- }
- } else {
- throw new InvalidAlgorithmParameterException(
- "Unsupported params class: " + params.getClass().getName()
- + ". Supported: " + KeyGenParameterSpec.class.getName()
- + ", " + KeyPairGeneratorSpec.class.getName());
- }
-
- mEntryAlias = spec.getKeystoreAlias();
- mEntryUid = spec.getUid();
- mSpec = spec;
- mKeymasterAlgorithm = keymasterAlgorithm;
- mEncryptionAtRestRequired = encryptionAtRestRequired;
- mKeySizeBits = spec.getKeySize();
- initAlgorithmSpecificParameters();
- if (mKeySizeBits == -1) {
- mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
- }
- checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
-
- if (spec.getKeystoreAlias() == null) {
- throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
- }
-
- String jcaKeyAlgorithm;
- try {
- jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
- keymasterAlgorithm);
- mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
- mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
- mKeymasterEncryptionPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
- spec.getEncryptionPaddings());
- if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (spec.isRandomizedEncryptionRequired())) {
- for (int keymasterPadding : mKeymasterEncryptionPaddings) {
- if (!KeymasterUtils
- .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
- keymasterPadding)) {
- throw new InvalidAlgorithmParameterException(
- "Randomized encryption (IND-CPA) required but may be violated"
- + " by padding scheme: "
- + KeyProperties.EncryptionPadding.fromKeymaster(
- keymasterPadding)
- + ". See " + KeyGenParameterSpec.class.getName()
- + " documentation.");
- }
- }
- }
- mKeymasterSignaturePaddings = KeyProperties.SignaturePadding.allToKeymaster(
- spec.getSignaturePaddings());
- if (spec.isDigestsSpecified()) {
- mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
- } else {
- mKeymasterDigests = EmptyArray.INT;
- }
-
- // Check that user authentication related parameters are acceptable. This method
- // will throw an IllegalStateException if there are issues (e.g., secure lock screen
- // not set up).
- KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
- } catch (IllegalArgumentException | IllegalStateException e) {
- throw new InvalidAlgorithmParameterException(e);
- }
-
- mJcaKeyAlgorithm = jcaKeyAlgorithm;
- mRng = random;
- mKeyStore = KeyStore.getInstance();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- private void resetAll() {
- mEntryAlias = null;
- mEntryUid = KeyStore.UID_SELF;
- mJcaKeyAlgorithm = null;
- mKeymasterAlgorithm = -1;
- mKeymasterPurposes = null;
- mKeymasterBlockModes = null;
- mKeymasterEncryptionPaddings = null;
- mKeymasterSignaturePaddings = null;
- mKeymasterDigests = null;
- mKeySizeBits = 0;
- mSpec = null;
- mRSAPublicExponent = null;
- mEncryptionAtRestRequired = false;
- mRng = null;
- mKeyStore = null;
- }
-
- private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
- AlgorithmParameterSpec algSpecificSpec = mSpec.getAlgorithmParameterSpec();
- switch (mKeymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_RSA:
- {
- BigInteger publicExponent = null;
- if (algSpecificSpec instanceof RSAKeyGenParameterSpec) {
- RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algSpecificSpec;
- if (mKeySizeBits == -1) {
- mKeySizeBits = rsaSpec.getKeysize();
- } else if (mKeySizeBits != rsaSpec.getKeysize()) {
- throw new InvalidAlgorithmParameterException("RSA key size must match "
- + " between " + mSpec + " and " + algSpecificSpec
- + ": " + mKeySizeBits + " vs " + rsaSpec.getKeysize());
- }
- publicExponent = rsaSpec.getPublicExponent();
- } else if (algSpecificSpec != null) {
- throw new InvalidAlgorithmParameterException(
- "RSA may only use RSAKeyGenParameterSpec");
- }
- if (publicExponent == null) {
- publicExponent = RSAKeyGenParameterSpec.F4;
- }
- if (publicExponent.compareTo(BigInteger.ZERO) < 1) {
- throw new InvalidAlgorithmParameterException(
- "RSA public exponent must be positive: " + publicExponent);
- }
- if (publicExponent.compareTo(KeymasterArguments.UINT64_MAX_VALUE) > 0) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported RSA public exponent: " + publicExponent
- + ". Maximum supported value: " + KeymasterArguments.UINT64_MAX_VALUE);
- }
- mRSAPublicExponent = publicExponent;
- break;
- }
- case KeymasterDefs.KM_ALGORITHM_EC:
- if (algSpecificSpec instanceof ECGenParameterSpec) {
- ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
- String curveName = ecSpec.getName();
- Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
- curveName.toLowerCase(Locale.US));
- if (ecSpecKeySizeBits == null) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported EC curve name: " + curveName
- + ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
- }
- if (mKeySizeBits == -1) {
- mKeySizeBits = ecSpecKeySizeBits;
- } else if (mKeySizeBits != ecSpecKeySizeBits) {
- throw new InvalidAlgorithmParameterException("EC key size must match "
- + " between " + mSpec + " and " + algSpecificSpec
- + ": " + mKeySizeBits + " vs " + ecSpecKeySizeBits);
- }
- } else if (algSpecificSpec != null) {
- throw new InvalidAlgorithmParameterException(
- "EC may only use ECGenParameterSpec");
- }
- break;
- default:
- throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
- }
- }
-
- @Override
- public KeyPair generateKeyPair() {
- if (mKeyStore == null || mSpec == null) {
- throw new IllegalStateException("Not initialized");
- }
-
- int flags = (mEncryptionAtRestRequired) ? KeyStore.FLAG_ENCRYPTED : 0;
- if (((flags & KeyStore.FLAG_ENCRYPTED) != 0)
- && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
- throw new IllegalStateException(
- "Encryption at rest using secure lock screen credential requested for key pair"
- + ", but the user has not yet entered the credential");
- }
-
- if (mSpec.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
- }
- if (mSpec.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
- }
-
- byte[] additionalEntropy =
- KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- mRng, (mKeySizeBits + 7) / 8);
-
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
- final String privateKeyAlias = Credentials.USER_PRIVATE_KEY + mEntryAlias;
- boolean success = false;
- try {
- generateKeystoreKeyPair(
- privateKeyAlias, constructKeyGenerationArguments(), additionalEntropy, flags);
- KeyPair keyPair = loadKeystoreKeyPair(privateKeyAlias);
-
- storeCertificateChain(flags, createCertificateChain(privateKeyAlias, keyPair));
-
- success = true;
- return keyPair;
- } catch (ProviderException | IllegalArgumentException | DeviceIdAttestationException e) {
- if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
- throw new SecureKeyImportUnavailableException(e);
- } else {
- throw new ProviderException(e);
- }
- } finally {
- if (!success) {
- Credentials.deleteAllTypesForAlias(mKeyStore, mEntryAlias, mEntryUid);
- }
- }
- }
-
- private Iterable<byte[]> createCertificateChain(final String privateKeyAlias, KeyPair keyPair)
- throws ProviderException, DeviceIdAttestationException {
- byte[] challenge = mSpec.getAttestationChallenge();
- if (challenge != null) {
- KeymasterArguments args = new KeymasterArguments();
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, challenge);
-
- if (mSpec.isDevicePropertiesAttestationIncluded()) {
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND,
- Build.BRAND.getBytes(StandardCharsets.UTF_8));
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE,
- Build.DEVICE.getBytes(StandardCharsets.UTF_8));
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT,
- Build.PRODUCT.getBytes(StandardCharsets.UTF_8));
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER,
- Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8));
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL,
- Build.MODEL.getBytes(StandardCharsets.UTF_8));
- }
-
- int[] idTypes = mSpec.getAttestationIds();
- if (idTypes != null) {
- final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
- for (int idType : idTypes) {
- idTypesSet.add(idType);
- }
- TelephonyManager telephonyService = null;
- if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI)
- || idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) {
- telephonyService =
- (TelephonyManager) KeyStore.getApplicationContext().getSystemService(
- Context.TELEPHONY_SERVICE);
- if (telephonyService == null) {
- throw new DeviceIdAttestationException(
- "Unable to access telephony service");
- }
- }
- for (final Integer idType : idTypesSet) {
- switch (idType) {
- case AttestationUtils.ID_TYPE_SERIAL:
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_SERIAL,
- Build.getSerial().getBytes(StandardCharsets.UTF_8)
- );
- break;
- case AttestationUtils.ID_TYPE_IMEI: {
- final String imei = telephonyService.getImei(0);
- if (imei == null) {
- throw new DeviceIdAttestationException("Unable to retrieve IMEI");
- }
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_IMEI,
- imei.getBytes(StandardCharsets.UTF_8)
- );
- break;
- }
- case AttestationUtils.ID_TYPE_MEID: {
- final String meid = telephonyService.getMeid(0);
- if (meid == null) {
- throw new DeviceIdAttestationException("Unable to retrieve MEID");
- }
- args.addBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MEID,
- meid.getBytes(StandardCharsets.UTF_8)
- );
- break;
- }
- case AttestationUtils.USE_INDIVIDUAL_ATTESTATION: {
- args.addBoolean(KeymasterDefs.KM_TAG_DEVICE_UNIQUE_ATTESTATION);
- break;
- }
- default:
- throw new IllegalArgumentException("Unknown device ID type " + idType);
- }
- }
- }
-
- return getAttestationChain(privateKeyAlias, keyPair, args);
- }
-
- // Very short certificate chain in the non-attestation case.
- return Collections.singleton(generateSelfSignedCertificateBytes(keyPair));
- }
-
- private void generateKeystoreKeyPair(final String privateKeyAlias, KeymasterArguments args,
- byte[] additionalEntropy, final int flags) throws ProviderException {
- KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
- int errorCode = mKeyStore.generateKey(privateKeyAlias, args, additionalEntropy,
- mEntryUid, flags, resultingKeyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- if (errorCode == KeyStore.HARDWARE_TYPE_UNAVAILABLE) {
- throw new StrongBoxUnavailableException("Failed to generate key pair");
- } else {
- throw new ProviderException(
- "Failed to generate key pair", KeyStore.getKeyStoreException(errorCode));
- }
- }
- }
-
- private KeyPair loadKeystoreKeyPair(final String privateKeyAlias) throws ProviderException {
- try {
- KeyPair result = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
- mKeyStore, privateKeyAlias, mEntryUid);
- if (!mJcaKeyAlgorithm.equalsIgnoreCase(result.getPrivate().getAlgorithm())) {
- throw new ProviderException(
- "Generated key pair algorithm does not match requested algorithm: "
- + result.getPrivate().getAlgorithm() + " vs " + mJcaKeyAlgorithm);
- }
- return result;
- } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
- throw new ProviderException("Failed to load generated key pair from keystore", e);
- }
- }
-
- private KeymasterArguments constructKeyGenerationArguments()
- throws IllegalArgumentException, DeviceIdAttestationException {
- KeymasterArguments args = new KeymasterArguments();
- args.addUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
- args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterEncryptionPaddings);
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-
- KeymasterUtils.addUserAuthArgs(args, mSpec);
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- mSpec.getKeyValidityForOriginationEnd());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- mSpec.getKeyValidityForConsumptionEnd());
- addAlgorithmSpecificParameters(args);
-
- if (mSpec.isUniqueIdIncluded()) {
- args.addBoolean(KeymasterDefs.KM_TAG_INCLUDE_UNIQUE_ID);
- }
- return args;
- }
-
- private void storeCertificateChain(final int flags, Iterable<byte[]> iterable)
- throws ProviderException {
- Iterator<byte[]> iter = iterable.iterator();
- storeCertificate(
- Credentials.USER_CERTIFICATE, iter.next(), flags, "Failed to store certificate");
-
- if (!iter.hasNext()) {
- return;
- }
-
- ByteArrayOutputStream certificateConcatenationStream = new ByteArrayOutputStream();
- while (iter.hasNext()) {
- byte[] data = iter.next();
- certificateConcatenationStream.write(data, 0, data.length);
- }
-
- storeCertificate(Credentials.CA_CERTIFICATE, certificateConcatenationStream.toByteArray(),
- flags, "Failed to store attestation CA certificate");
- }
-
- private void storeCertificate(String prefix, byte[] certificateBytes, final int flags,
- String failureMessage) throws ProviderException {
- int insertErrorCode = mKeyStore.insert(
- prefix + mEntryAlias,
- certificateBytes,
- mEntryUid,
- flags);
- if (insertErrorCode != KeyStore.NO_ERROR) {
- throw new ProviderException(failureMessage,
- KeyStore.getKeyStoreException(insertErrorCode));
- }
- }
-
- private byte[] generateSelfSignedCertificateBytes(KeyPair keyPair) throws ProviderException {
- try {
- return generateSelfSignedCertificate(keyPair.getPrivate(), keyPair.getPublic())
- .getEncoded();
- } catch (IOException | CertificateParsingException e) {
- throw new ProviderException("Failed to generate self-signed certificate", e);
- } catch (CertificateEncodingException e) {
- throw new ProviderException(
- "Failed to obtain encoded form of self-signed certificate", e);
- }
- }
-
- private Iterable<byte[]> getAttestationChain(String privateKeyAlias,
- KeyPair keyPair, KeymasterArguments args)
- throws ProviderException {
- final KeymasterCertificateChain outChain = new KeymasterCertificateChain();
- final int errorCode;
- if (mSpec.isDevicePropertiesAttestationIncluded()
- && mSpec.getAttestationChallenge() == null) {
- throw new ProviderException("An attestation challenge must be provided when requesting "
- + "device properties attestation.");
- }
- errorCode = mKeyStore.attestKey(privateKeyAlias, args, outChain);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new ProviderException("Failed to generate attestation certificate chain",
- KeyStore.getKeyStoreException(errorCode));
- }
- Collection<byte[]> chain = outChain.getCertificates();
- if (chain.size() < 2) {
- throw new ProviderException("Attestation certificate chain contained "
- + chain.size() + " entries. At least two are required.");
- }
- return chain;
- }
-
- private void addAlgorithmSpecificParameters(KeymasterArguments keymasterArgs) {
- switch (mKeymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_RSA:
- keymasterArgs.addUnsignedLong(
- KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT, mRSAPublicExponent);
- break;
- case KeymasterDefs.KM_ALGORITHM_EC:
- break;
- default:
- throw new ProviderException("Unsupported algorithm: " + mKeymasterAlgorithm);
- }
- }
-
- private X509Certificate generateSelfSignedCertificate(PrivateKey privateKey,
- PublicKey publicKey) throws CertificateParsingException, IOException {
- String signatureAlgorithm =
- getCertificateSignatureAlgorithm(mKeymasterAlgorithm, mKeySizeBits, mSpec);
- if (signatureAlgorithm == null) {
- // Key cannot be used to sign a certificate
- return generateSelfSignedCertificateWithFakeSignature(publicKey);
- } else {
- // Key can be used to sign a certificate
- try {
- return generateSelfSignedCertificateWithValidSignature(
- privateKey, publicKey, signatureAlgorithm);
- } catch (Exception e) {
- // Failed to generate the self-signed certificate with valid signature. Fall back
- // to generating a self-signed certificate with a fake signature. This is done for
- // all exception types because we prefer key pair generation to succeed and end up
- // producing a self-signed certificate with an invalid signature to key pair
- // generation failing.
- return generateSelfSignedCertificateWithFakeSignature(publicKey);
- }
- }
- }
-
- @SuppressWarnings("deprecation")
- private X509Certificate generateSelfSignedCertificateWithValidSignature(
- PrivateKey privateKey, PublicKey publicKey, String signatureAlgorithm) throws Exception {
- final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
- certGen.setPublicKey(publicKey);
- certGen.setSerialNumber(mSpec.getCertificateSerialNumber());
- certGen.setSubjectDN(mSpec.getCertificateSubject());
- certGen.setIssuerDN(mSpec.getCertificateSubject());
- certGen.setNotBefore(mSpec.getCertificateNotBefore());
- certGen.setNotAfter(mSpec.getCertificateNotAfter());
- certGen.setSignatureAlgorithm(signatureAlgorithm);
- return certGen.generate(privateKey);
- }
-
- @SuppressWarnings("deprecation")
- private X509Certificate generateSelfSignedCertificateWithFakeSignature(
- PublicKey publicKey) throws IOException, CertificateParsingException {
- V3TBSCertificateGenerator tbsGenerator = new V3TBSCertificateGenerator();
- ASN1ObjectIdentifier sigAlgOid;
- AlgorithmIdentifier sigAlgId;
- byte[] signature;
- switch (mKeymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
- sigAlgId = new AlgorithmIdentifier(sigAlgOid);
- ASN1EncodableVector v = new ASN1EncodableVector();
- v.add(new ASN1Integer(BigInteger.valueOf(0)));
- v.add(new ASN1Integer(BigInteger.valueOf(0)));
- signature = new DERSequence().getEncoded();
- break;
- case KeymasterDefs.KM_ALGORITHM_RSA:
- sigAlgOid = PKCSObjectIdentifiers.sha256WithRSAEncryption;
- sigAlgId = new AlgorithmIdentifier(sigAlgOid, DERNull.INSTANCE);
- signature = new byte[1];
- break;
- default:
- throw new ProviderException("Unsupported key algorithm: " + mKeymasterAlgorithm);
- }
-
- try (ASN1InputStream publicKeyInfoIn = new ASN1InputStream(publicKey.getEncoded())) {
- tbsGenerator.setSubjectPublicKeyInfo(
- SubjectPublicKeyInfo.getInstance(publicKeyInfoIn.readObject()));
- }
- tbsGenerator.setSerialNumber(new ASN1Integer(mSpec.getCertificateSerialNumber()));
- X509Principal subject =
- new X509Principal(mSpec.getCertificateSubject().getEncoded());
- tbsGenerator.setSubject(subject);
- tbsGenerator.setIssuer(subject);
- tbsGenerator.setStartDate(new Time(mSpec.getCertificateNotBefore()));
- tbsGenerator.setEndDate(new Time(mSpec.getCertificateNotAfter()));
- tbsGenerator.setSignature(sigAlgId);
- TBSCertificate tbsCertificate = tbsGenerator.generateTBSCertificate();
-
- ASN1EncodableVector result = new ASN1EncodableVector();
- result.add(tbsCertificate);
- result.add(sigAlgId);
- result.add(new DERBitString(signature));
- return new X509CertificateObject(Certificate.getInstance(new DERSequence(result)));
- }
-
- private static int getDefaultKeySize(int keymasterAlgorithm) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- return EC_DEFAULT_KEY_SIZE;
- case KeymasterDefs.KM_ALGORITHM_RSA:
- return RSA_DEFAULT_KEY_SIZE;
- default:
- throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- private static void checkValidKeySize(
- int keymasterAlgorithm,
- int keySize,
- boolean isStrongBoxBacked)
- throws InvalidAlgorithmParameterException {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- if (isStrongBoxBacked && keySize != 256) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported StrongBox EC key size: "
- + keySize + " bits. Supported: 256");
- }
- if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) {
- throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
- + keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
- }
- break;
- case KeymasterDefs.KM_ALGORITHM_RSA:
- if (keySize < RSA_MIN_KEY_SIZE || keySize > RSA_MAX_KEY_SIZE) {
- throw new InvalidAlgorithmParameterException("RSA key size must be >= "
- + RSA_MIN_KEY_SIZE + " and <= " + RSA_MAX_KEY_SIZE);
- }
- break;
- default:
- throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- /**
- * Returns the {@code Signature} algorithm to be used for signing a certificate using the
- * specified key or {@code null} if the key cannot be used for signing a certificate.
- */
- @Nullable
- private static String getCertificateSignatureAlgorithm(
- int keymasterAlgorithm,
- int keySizeBits,
- KeyGenParameterSpec spec) {
- // Constraints:
- // 1. Key must be authorized for signing without user authentication.
- // 2. Signature digest must be one of key's authorized digests.
- // 3. For RSA keys, the digest output size must not exceed modulus size minus space overhead
- // of RSA PKCS#1 signature padding scheme (about 30 bytes).
- // 4. For EC keys, the there is no point in using a digest whose output size is longer than
- // key/field size because the digest will be truncated to that size.
-
- if ((spec.getPurposes() & KeyProperties.PURPOSE_SIGN) == 0) {
- // Key not authorized for signing
- return null;
- }
- if (spec.isUserAuthenticationRequired()) {
- // Key not authorized for use without user authentication
- return null;
- }
- if (!spec.isDigestsSpecified()) {
- // Key not authorized for any digests -- can't sign
- return null;
- }
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_EC:
- {
- Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
- spec.getDigests(),
- AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
-
- int bestKeymasterDigest = -1;
- int bestDigestOutputSizeBits = -1;
- for (int keymasterDigest : availableKeymasterDigests) {
- int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
- if (outputSizeBits == keySizeBits) {
- // Perfect match -- use this digest
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- break;
- }
- // Not a perfect match -- check against the best digest so far
- if (bestKeymasterDigest == -1) {
- // First digest tested -- definitely the best so far
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- } else {
- // Prefer output size to be as close to key size as possible, with output
- // sizes larger than key size preferred to those smaller than key size.
- if (bestDigestOutputSizeBits < keySizeBits) {
- // Output size of the best digest so far is smaller than key size.
- // Anything larger is a win.
- if (outputSizeBits > bestDigestOutputSizeBits) {
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- }
- } else {
- // Output size of the best digest so far is larger than key size.
- // Anything smaller is a win, as long as it's not smaller than key size.
- if ((outputSizeBits < bestDigestOutputSizeBits)
- && (outputSizeBits >= keySizeBits)) {
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- }
- }
- }
- }
- if (bestKeymasterDigest == -1) {
- return null;
- }
- return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
- bestKeymasterDigest) + "WithECDSA";
- }
- case KeymasterDefs.KM_ALGORITHM_RSA:
- {
- // Check whether this key is authorized for PKCS#1 signature padding.
- // We use Bouncy Castle to generate self-signed RSA certificates. Bouncy Castle
- // only supports RSA certificates signed using PKCS#1 padding scheme. The key needs
- // to be authorized for PKCS#1 padding or padding NONE which means any padding.
- boolean pkcs1SignaturePaddingSupported =
- com.android.internal.util.ArrayUtils.contains(
- KeyProperties.SignaturePadding.allToKeymaster(
- spec.getSignaturePaddings()),
- KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
- if (!pkcs1SignaturePaddingSupported) {
- // Key not authorized for PKCS#1 signature padding -- can't sign
- return null;
- }
-
- Set<Integer> availableKeymasterDigests = getAvailableKeymasterSignatureDigests(
- spec.getDigests(),
- AndroidKeyStoreBCWorkaroundProvider.getSupportedEcdsaSignatureDigests());
-
- // The amount of space available for the digest is less than modulus size by about
- // 30 bytes because padding must be at least 11 bytes long (00 || 01 || PS || 00,
- // where PS must be at least 8 bytes long), and then there's also the 15--19 bytes
- // overhead (depending the on chosen digest) for encoding digest OID and digest
- // value in DER.
- int maxDigestOutputSizeBits = keySizeBits - 30 * 8;
- int bestKeymasterDigest = -1;
- int bestDigestOutputSizeBits = -1;
- for (int keymasterDigest : availableKeymasterDigests) {
- int outputSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
- if (outputSizeBits > maxDigestOutputSizeBits) {
- // Digest too long (signature generation will fail) -- skip
- continue;
- }
- if (bestKeymasterDigest == -1) {
- // First digest tested -- definitely the best so far
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- } else {
- // The longer the better
- if (outputSizeBits > bestDigestOutputSizeBits) {
- bestKeymasterDigest = keymasterDigest;
- bestDigestOutputSizeBits = outputSizeBits;
- }
- }
- }
- if (bestKeymasterDigest == -1) {
- return null;
- }
- return KeyProperties.Digest.fromKeymasterToSignatureAlgorithmDigest(
- bestKeymasterDigest) + "WithRSA";
- }
- default:
- throw new ProviderException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- private static Set<Integer> getAvailableKeymasterSignatureDigests(
- @KeyProperties.DigestEnum String[] authorizedKeyDigests,
- @KeyProperties.DigestEnum String[] supportedSignatureDigests) {
- Set<Integer> authorizedKeymasterKeyDigests = new HashSet<Integer>();
- for (int keymasterDigest : KeyProperties.Digest.allToKeymaster(authorizedKeyDigests)) {
- authorizedKeymasterKeyDigests.add(keymasterDigest);
- }
- Set<Integer> supportedKeymasterSignatureDigests = new HashSet<Integer>();
- for (int keymasterDigest
- : KeyProperties.Digest.allToKeymaster(supportedSignatureDigests)) {
- supportedKeymasterSignatureDigests.add(keymasterDigest);
- }
- Set<Integer> result = new HashSet<Integer>(supportedKeymasterSignatureDigests);
- result.retainAll(authorizedKeymasterKeyDigests);
- return result;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java b/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java
deleted file mode 100644
index 45d579e..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreLoadStoreParameter.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.KeyStore;
-import java.security.KeyStore.ProtectionParameter;
-
-class AndroidKeyStoreLoadStoreParameter implements KeyStore.LoadStoreParameter {
-
- private final int mUid;
-
- AndroidKeyStoreLoadStoreParameter(int uid) {
- mUid = uid;
- }
-
- @Override
- public ProtectionParameter getProtectionParameter() {
- return null;
- }
-
- int getUid() {
- return mUid;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
deleted file mode 100644
index 06e4c88..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.PrivateKey;
-
-/**
- * {@link PrivateKey} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
-
- public AndroidKeyStorePrivateKey(String alias, int uid, String algorithm) {
- super(alias, uid, algorithm);
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 0871517..ecb082e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -20,30 +20,13 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.security.KeyStore;
-import android.security.keymaster.ExportResult;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterDefs;
import java.io.IOException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
-import java.security.ProviderException;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.Signature;
-import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.List;
import javax.crypto.Cipher;
import javax.crypto.Mac;
@@ -57,117 +40,10 @@
public class AndroidKeyStoreProvider extends Provider {
private static final String PROVIDER_NAME = "AndroidKeyStore";
- // IMPLEMENTATION NOTE: Class names are hard-coded in this provider to avoid loading these
- // classes when this provider is instantiated and installed early on during each app's
- // initialization process.
- //
- // Crypto operations operating on the AndroidKeyStore keys must not be offered by this provider.
- // Instead, they need to be offered by AndroidKeyStoreBCWorkaroundProvider. See its Javadoc
- // for details.
-
- private static final String PACKAGE_NAME = "android.security.keystore";
-
- private static final String DESEDE_SYSTEM_PROPERTY =
- "ro.hardware.keystore_desede";
-
/** @hide */
- public AndroidKeyStoreProvider() {
- this(PROVIDER_NAME);
- }
-
- /** @hide **/
- public AndroidKeyStoreProvider(String providerName) {
- super(providerName, 1.0, "Android KeyStore security provider");
-
- boolean supports3DES = "true".equals(android.os.SystemProperties.get(DESEDE_SYSTEM_PROPERTY));
-
- // java.security.KeyStore
- put("KeyStore." + providerName, PACKAGE_NAME + ".AndroidKeyStoreSpi");
-
- // java.security.KeyPairGenerator
- put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
- put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
-
- // java.security.KeyFactory
- putKeyFactoryImpl("EC");
- putKeyFactoryImpl("RSA");
-
- // javax.crypto.KeyGenerator
- put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
- put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
- put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
- put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
- put("KeyGenerator.HmacSHA384", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA384");
- put("KeyGenerator.HmacSHA512", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA512");
-
- if (supports3DES) {
- put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
- }
-
- // java.security.SecretKeyFactory
- putSecretKeyFactoryImpl("AES");
- if (supports3DES) {
- putSecretKeyFactoryImpl("DESede");
- }
- putSecretKeyFactoryImpl("HmacSHA1");
- putSecretKeyFactoryImpl("HmacSHA224");
- putSecretKeyFactoryImpl("HmacSHA256");
- putSecretKeyFactoryImpl("HmacSHA384");
- putSecretKeyFactoryImpl("HmacSHA512");
- }
-
- /**
- * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
- * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
- * the platform property that indicates that Keystore 2.0 is enabled is not readable
- * by applications. So we set this value when {@code install()} is called because it
- * is called by zygote, which can access Keystore2Properties.
- *
- * This function can be removed once the transition to Keystore 2.0 is complete.
- * b/171305684
- *
- * @return true if Keystore 2.0 is enabled.
- * @hide
- */
- public static boolean isKeystore2Enabled() {
- return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
- }
-
- /**
- * Installs a new instance of this provider (and the
- * {@link AndroidKeyStoreBCWorkaroundProvider}).
- * @hide
- */
- public static void install() {
- Provider[] providers = Security.getProviders();
- int bcProviderIndex = -1;
- for (int i = 0; i < providers.length; i++) {
- Provider provider = providers[i];
- if ("BC".equals(provider.getName())) {
- bcProviderIndex = i;
- break;
- }
- }
-
- Security.addProvider(new AndroidKeyStoreProvider());
- Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
- if (bcProviderIndex != -1) {
- // Bouncy Castle provider found -- install the workaround provider above it.
- // insertProviderAt uses 1-based positions.
- Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
- } else {
- // Bouncy Castle provider not found -- install the workaround provider at lowest
- // priority.
- Security.addProvider(workaroundProvider);
- }
- }
-
- private void putSecretKeyFactoryImpl(String algorithm) {
- put("SecretKeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreSecretKeyFactorySpi");
- }
-
- private void putKeyFactoryImpl(String algorithm) {
- put("KeyFactory." + algorithm, PACKAGE_NAME + ".AndroidKeyStoreKeyFactorySpi");
+ public AndroidKeyStoreProvider(@NonNull String name) {
+ super(name, 1.0, "Android KeyStore security provider");
+ throw new IllegalStateException("Should not be instantiated.");
}
/**
@@ -189,229 +65,7 @@
if (cryptoPrimitive == null) {
throw new NullPointerException();
}
- Object spi;
- if (cryptoPrimitive instanceof Signature) {
- spi = ((Signature) cryptoPrimitive).getCurrentSpi();
- } else if (cryptoPrimitive instanceof Mac) {
- spi = ((Mac) cryptoPrimitive).getCurrentSpi();
- } else if (cryptoPrimitive instanceof Cipher) {
- spi = ((Cipher) cryptoPrimitive).getCurrentSpi();
- } else {
- throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive
- + ". Supported: Signature, Mac, Cipher");
- }
- if (spi == null) {
- throw new IllegalStateException("Crypto primitive not initialized");
- } else if (!(spi instanceof KeyStoreCryptoOperation)) {
- throw new IllegalArgumentException(
- "Crypto primitive not backed by AndroidKeyStore provider: " + cryptoPrimitive
- + ", spi: " + spi);
- }
- return ((KeyStoreCryptoOperation) spi).getOperationHandle();
- }
-
- /** @hide **/
- @NonNull
- public static AndroidKeyStorePublicKey getAndroidKeyStorePublicKey(
- @NonNull String alias,
- int uid,
- @NonNull @KeyProperties.KeyAlgorithmEnum String keyAlgorithm,
- @NonNull byte[] x509EncodedForm) {
- PublicKey publicKey;
- try {
- KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
- publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(x509EncodedForm));
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain " + keyAlgorithm + " KeyFactory", e);
- } catch (InvalidKeySpecException e) {
- throw new ProviderException("Invalid X.509 encoding of public key", e);
- }
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreECPublicKey(alias, uid, (ECPublicKey) publicKey);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreRSAPublicKey(alias, uid, (RSAPublicKey) publicKey);
- } else {
- throw new ProviderException("Unsupported Android Keystore public key algorithm: "
- + keyAlgorithm);
- }
- }
-
- @NonNull
- private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
- @NonNull AndroidKeyStorePublicKey publicKey) {
- String keyAlgorithm = publicKey.getAlgorithm();
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreECPrivateKey(
- publicKey.getAlias(), publicKey.getUid(), ((ECKey) publicKey).getParams());
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- return new AndroidKeyStoreRSAPrivateKey(
- publicKey.getAlias(), publicKey.getUid(), ((RSAKey) publicKey).getModulus());
- } else {
- throw new ProviderException("Unsupported Android Keystore public key algorithm: "
- + keyAlgorithm);
- }
- }
-
- @NonNull
- private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore,
- @NonNull String alias, int uid)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = keyStore.getKeyCharacteristics(
- alias, null, null, uid, keyCharacteristics);
- if (errorCode == KeyStore.KEY_PERMANENTLY_INVALIDATED) {
- throw (KeyPermanentlyInvalidatedException)
- new KeyPermanentlyInvalidatedException(
- "User changed or deleted their auth credentials",
- KeyStore.getKeyStoreException(errorCode));
- }
- if (errorCode != KeyStore.NO_ERROR) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain information about key")
- .initCause(KeyStore.getKeyStoreException(errorCode));
- }
- return keyCharacteristics;
- }
-
- @NonNull
- private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
- KeyCharacteristics keyCharacteristics)
- throws UnrecoverableKeyException {
- ExportResult exportResult = keyStore.exportKey(
- privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
- if (exportResult.resultCode != KeyStore.NO_ERROR) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to obtain X.509 form of public key")
- .initCause(KeyStore.getKeyStoreException(exportResult.resultCode));
- }
- final byte[] x509EncodedPublicKey = exportResult.exportData;
-
- Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
- if (keymasterAlgorithm == null) {
- throw new UnrecoverableKeyException("Key algorithm unknown");
- }
-
- String jcaKeyAlgorithm;
- try {
- jcaKeyAlgorithm = KeyProperties.KeyAlgorithm.fromKeymasterAsymmetricKeyAlgorithm(
- keymasterAlgorithm);
- } catch (IllegalArgumentException e) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Failed to load private key")
- .initCause(e);
- }
-
- return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- privateKeyAlias, uid, jcaKeyAlgorithm, x509EncodedPublicKey);
- }
-
- /** @hide **/
- @NonNull
- public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
- getKeyCharacteristics(keyStore, privateKeyAlias, uid));
- }
-
- @NonNull
- private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
- @NonNull KeyCharacteristics keyCharacteristics)
- throws UnrecoverableKeyException {
- AndroidKeyStorePublicKey publicKey =
- loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
- keyCharacteristics);
- AndroidKeyStorePrivateKey privateKey =
- AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
- return new KeyPair(publicKey, privateKey);
- }
-
- /** @hide **/
- @NonNull
- public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
- getKeyCharacteristics(keyStore, privateKeyAlias, uid));
- }
-
- @NonNull
- private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
- @NonNull KeyCharacteristics keyCharacteristics)
- throws UnrecoverableKeyException {
- KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
- keyCharacteristics);
- return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
- }
-
- /** @hide **/
- @NonNull
- public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid,
- getKeyCharacteristics(keyStore, privateKeyAlias, uid));
- }
-
- @NonNull
- private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
- @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)
- throws UnrecoverableKeyException {
- Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
- if (keymasterAlgorithm == null) {
- throw new UnrecoverableKeyException("Key algorithm unknown");
- }
-
- List<Integer> keymasterDigests = keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST);
- int keymasterDigest;
- if (keymasterDigests.isEmpty()) {
- keymasterDigest = -1;
- } else {
- // More than one digest can be permitted for this key. Use the first one to form the
- // JCA key algorithm name.
- keymasterDigest = keymasterDigests.get(0);
- }
-
- @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
- try {
- keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
- keymasterAlgorithm, keymasterDigest);
- } catch (IllegalArgumentException e) {
- throw (UnrecoverableKeyException)
- new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
- }
-
- return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
- }
-
- /** @hide **/
- @NonNull
- public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
- @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
- throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
- KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid);
-
- Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
- if (keymasterAlgorithm == null) {
- throw new UnrecoverableKeyException("Key algorithm unknown");
- }
-
- if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
- keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES ||
- keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
- return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
- keyCharacteristics);
- } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
- keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
- return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid,
- keyCharacteristics);
- } else {
- throw new UnrecoverableKeyException("Key algorithm unknown");
- }
+ return 0;
}
/**
@@ -434,13 +88,9 @@
@NonNull
public static java.security.KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- final java.security.KeyStore.LoadStoreParameter loadParameter;
- if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
- loadParameter = new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
- KeyProperties.legacyUidToNamespace(uid));
- } else {
- loadParameter = new AndroidKeyStoreLoadStoreParameter(uid);
- }
+ final java.security.KeyStore.LoadStoreParameter loadParameter =
+ new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.legacyUidToNamespace(uid));
java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
try {
result.load(loadParameter);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
deleted file mode 100644
index 4194780..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.PublicKey;
-import java.util.Arrays;
-
-/**
- * {@link PublicKey} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
-
- private final byte[] mEncoded;
-
- public AndroidKeyStorePublicKey(String alias, int uid, String algorithm, byte[] x509EncodedForm) {
- super(alias, uid, algorithm);
- mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
- }
-
- @Override
- public String getFormat() {
- return "X.509";
- }
-
- @Override
- public byte[] getEncoded() {
- return ArrayUtils.cloneIfNotEmpty(mEncoded);
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = super.hashCode();
- result = prime * result + Arrays.hashCode(mEncoded);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!super.equals(obj)) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- AndroidKeyStorePublicKey other = (AndroidKeyStorePublicKey) obj;
- if (!Arrays.equals(mEncoded, other.mEncoded)) {
- return false;
- }
- return true;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
deleted file mode 100644
index 2ae68fa..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.ProviderException;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-import java.security.spec.MGF1ParameterSpec;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherSpi;
-import javax.crypto.spec.OAEPParameterSpec;
-import javax.crypto.spec.PSource;
-
-/**
- * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
- *
- * @hide
- */
-abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
-
- /**
- * Raw RSA cipher without any padding.
- */
- public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
-
- @Override
- protected boolean adjustConfigForEncryptingWithPrivateKey() {
- // RSA encryption with no padding using private key is a way to implement raw RSA
- // signatures which JCA does not expose via Signature. We thus have to support this.
- setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
- return true;
- }
-
- @Override
- protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
-
- @Override
- protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
- if (params != null) {
- throw new InvalidAlgorithmParameterException(
- "Unexpected parameters: " + params + ". No parameters supported");
- }
- }
-
- @Override
- protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
-
- if (params != null) {
- throw new InvalidAlgorithmParameterException(
- "Unexpected parameters: " + params + ". No parameters supported");
- }
- }
-
- @Override
- protected AlgorithmParameters engineGetParameters() {
- return null;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- return 0;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForFinish() {
- return 0;
- }
- }
-
- /**
- * RSA cipher with PKCS#1 v1.5 encryption padding.
- */
- public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
- public PKCS1Padding() {
- super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
- }
-
- @Override
- protected boolean adjustConfigForEncryptingWithPrivateKey() {
- // RSA encryption with PCKS#1 padding using private key is a way to implement RSA
- // signatures with PKCS#1 padding. We have to support this for legacy reasons.
- setKeymasterPurposeOverride(KeymasterDefs.KM_PURPOSE_SIGN);
- setKeymasterPaddingOverride(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
- return true;
- }
-
- @Override
- protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
-
- @Override
- protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
- if (params != null) {
- throw new InvalidAlgorithmParameterException(
- "Unexpected parameters: " + params + ". No parameters supported");
- }
- }
-
- @Override
- protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
-
- if (params != null) {
- throw new InvalidAlgorithmParameterException(
- "Unexpected parameters: " + params + ". No parameters supported");
- }
- }
-
- @Override
- protected AlgorithmParameters engineGetParameters() {
- return null;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- return 0;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForFinish() {
- return (isEncrypting()) ? getModulusSizeBytes() : 0;
- }
- }
-
- /**
- * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
- */
- abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
-
- private static final String MGF_ALGORITGM_MGF1 = "MGF1";
-
- private int mKeymasterDigest = -1;
- private int mDigestOutputSizeBytes;
-
- OAEPWithMGF1Padding(int keymasterDigest) {
- super(KeymasterDefs.KM_PAD_RSA_OAEP);
- mKeymasterDigest = keymasterDigest;
- mDigestOutputSizeBytes =
- (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
-
- @Override
- protected final void initAlgorithmSpecificParameters(
- @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
- if (params == null) {
- return;
- }
-
- if (!(params instanceof OAEPParameterSpec)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported parameter spec: " + params
- + ". Only OAEPParameterSpec supported");
- }
- OAEPParameterSpec spec = (OAEPParameterSpec) params;
- if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported MGF: " + spec.getMGFAlgorithm()
- + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
- }
- String jcaDigest = spec.getDigestAlgorithm();
- int keymasterDigest;
- try {
- keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
- } catch (IllegalArgumentException e) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported digest: " + jcaDigest, e);
- }
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_SHA1:
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- // Permitted.
- break;
- default:
- throw new InvalidAlgorithmParameterException(
- "Unsupported digest: " + jcaDigest);
- }
- AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
- if (mgfParams == null) {
- throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
- }
- // Check whether MGF parameters match the OAEPParameterSpec
- if (!(mgfParams instanceof MGF1ParameterSpec)) {
- throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
- + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
- }
- MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
- String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
- if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported MGF1 digest: " + mgf1JcaDigest
- + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
- }
- PSource pSource = spec.getPSource();
- if (!(pSource instanceof PSource.PSpecified)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported source of encoding input P: " + pSource
- + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
- }
- PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
- byte[] pSourceValue = pSourceSpecified.getValue();
- if ((pSourceValue != null) && (pSourceValue.length > 0)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported source of encoding input P: " + pSource
- + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
- }
- mKeymasterDigest = keymasterDigest;
- mDigestOutputSizeBytes =
- (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
- if (params == null) {
- return;
- }
-
- OAEPParameterSpec spec;
- try {
- spec = params.getParameterSpec(OAEPParameterSpec.class);
- } catch (InvalidParameterSpecException e) {
- throw new InvalidAlgorithmParameterException("OAEP parameters required"
- + ", but not found in parameters: " + params, e);
- }
- if (spec == null) {
- throw new InvalidAlgorithmParameterException("OAEP parameters required"
- + ", but not provided in parameters: " + params);
- }
- initAlgorithmSpecificParameters(spec);
- }
-
- @Override
- protected final AlgorithmParameters engineGetParameters() {
- OAEPParameterSpec spec =
- new OAEPParameterSpec(
- KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
- MGF_ALGORITGM_MGF1,
- MGF1ParameterSpec.SHA1,
- PSource.PSpecified.DEFAULT);
- try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
- params.init(spec);
- return params;
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain OAEP AlgorithmParameters", e);
- } catch (InvalidParameterSpecException e) {
- throw new ProviderException(
- "Failed to initialize OAEP AlgorithmParameters with an IV",
- e);
- }
- }
-
- @Override
- protected final void addAlgorithmSpecificParametersToBegin(
- KeymasterArguments keymasterArgs) {
- super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- }
-
- @Override
- protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
- super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- return 0;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForFinish() {
- return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
- }
- }
-
- public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
- public OAEPWithSHA1AndMGF1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
- public OAEPWithSHA224AndMGF1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
- public OAEPWithSHA256AndMGF1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
- public OAEPWithSHA384AndMGF1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
- public OAEPWithSHA512AndMGF1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- private final int mKeymasterPadding;
- private int mKeymasterPaddingOverride;
-
- private int mModulusSizeBytes = -1;
-
- AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
- mKeymasterPadding = keymasterPadding;
- }
-
- @Override
- protected final void initKey(int opmode, Key key) throws InvalidKeyException {
- if (key == null) {
- throw new InvalidKeyException("Unsupported key: null");
- }
- if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
- + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
- }
- AndroidKeyStoreKey keystoreKey;
- if (key instanceof AndroidKeyStorePrivateKey) {
- keystoreKey = (AndroidKeyStoreKey) key;
- } else if (key instanceof AndroidKeyStorePublicKey) {
- keystoreKey = (AndroidKeyStoreKey) key;
- } else {
- throw new InvalidKeyException("Unsupported key type: " + key);
- }
-
- if (keystoreKey instanceof PrivateKey) {
- // Private key
- switch (opmode) {
- case Cipher.DECRYPT_MODE:
- case Cipher.UNWRAP_MODE:
- // Permitted
- break;
- case Cipher.ENCRYPT_MODE:
- case Cipher.WRAP_MODE:
- if (!adjustConfigForEncryptingWithPrivateKey()) {
- throw new InvalidKeyException(
- "RSA private keys cannot be used with " + opmodeToString(opmode)
- + " and padding "
- + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
- + ". Only RSA public keys supported for this mode");
- }
- break;
- default:
- throw new InvalidKeyException(
- "RSA private keys cannot be used with opmode: " + opmode);
- }
- } else {
- // Public key
- switch (opmode) {
- case Cipher.ENCRYPT_MODE:
- case Cipher.WRAP_MODE:
- // Permitted
- break;
- case Cipher.DECRYPT_MODE:
- case Cipher.UNWRAP_MODE:
- throw new InvalidKeyException(
- "RSA public keys cannot be used with " + opmodeToString(opmode)
- + " and padding "
- + KeyProperties.EncryptionPadding.fromKeymaster(mKeymasterPadding)
- + ". Only RSA private keys supported for this opmode.");
- // break;
- default:
- throw new InvalidKeyException(
- "RSA public keys cannot be used with " + opmodeToString(opmode));
- }
- }
-
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = getKeyStore().getKeyCharacteristics(
- keystoreKey.getAlias(), null, null, keystoreKey.getUid(), keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw getKeyStore().getInvalidKeyException(
- keystoreKey.getAlias(), keystoreKey.getUid(), errorCode);
- }
- long keySizeBits = keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
- if (keySizeBits == -1) {
- throw new InvalidKeyException("Size of key not known");
- } else if (keySizeBits > Integer.MAX_VALUE) {
- throw new InvalidKeyException("Key too large: " + keySizeBits + " bits");
- }
- mModulusSizeBytes = (int) ((keySizeBits + 7) / 8);
-
- setKey(keystoreKey);
- }
-
- /**
- * Adjusts the configuration of this cipher for encrypting using the private key.
- *
- * <p>The default implementation does nothing and refuses to adjust the configuration.
- *
- * @return {@code true} if the configuration has been adjusted, {@code false} if encrypting
- * using private key is not permitted for this cipher.
- */
- protected boolean adjustConfigForEncryptingWithPrivateKey() {
- return false;
- }
-
- @Override
- protected final void resetAll() {
- mModulusSizeBytes = -1;
- mKeymasterPaddingOverride = -1;
- super.resetAll();
- }
-
- @Override
- protected final void resetWhilePreservingInitState() {
- super.resetWhilePreservingInitState();
- }
-
- @Override
- protected void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- int keymasterPadding = getKeymasterPaddingOverride();
- if (keymasterPadding == -1) {
- keymasterPadding = mKeymasterPadding;
- }
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
- int purposeOverride = getKeymasterPurposeOverride();
- if ((purposeOverride != -1)
- && ((purposeOverride == KeymasterDefs.KM_PURPOSE_SIGN)
- || (purposeOverride == KeymasterDefs.KM_PURPOSE_VERIFY))) {
- // Keymaster sign/verify requires digest to be specified. For raw sign/verify it's NONE.
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
- }
- }
-
- @Override
- protected void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
- }
-
- @Override
- protected final int engineGetBlockSize() {
- // Not a block cipher, according to the RI
- return 0;
- }
-
- @Override
- protected final byte[] engineGetIV() {
- // IV never used
- return null;
- }
-
- @Override
- protected final int engineGetOutputSize(int inputLen) {
- return getModulusSizeBytes();
- }
-
- protected final int getModulusSizeBytes() {
- if (mModulusSizeBytes == -1) {
- throw new IllegalStateException("Not initialized");
- }
- return mModulusSizeBytes;
- }
-
- /**
- * Overrides the default padding of the crypto operation.
- */
- protected final void setKeymasterPaddingOverride(int keymasterPadding) {
- mKeymasterPaddingOverride = keymasterPadding;
- }
-
- protected final int getKeymasterPaddingOverride() {
- return mKeymasterPaddingOverride;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java
deleted file mode 100644
index adb3922..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPrivateKey.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.math.BigInteger;
-import java.security.PrivateKey;
-import java.security.interfaces.RSAKey;
-
-/**
- * RSA private key (instance of {@link PrivateKey} and {@link RSAKey}) backed by keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreRSAPrivateKey extends AndroidKeyStorePrivateKey implements RSAKey {
-
- private final BigInteger mModulus;
-
- public AndroidKeyStoreRSAPrivateKey(String alias, int uid, BigInteger modulus) {
- super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA);
- mModulus = modulus;
- }
-
- @Override
- public BigInteger getModulus() {
- return mModulus;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
deleted file mode 100644
index d85aace..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.math.BigInteger;
-import java.security.interfaces.RSAPublicKey;
-
-/**
- * {@link RSAPublicKey} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey {
- private final BigInteger mModulus;
- private final BigInteger mPublicExponent;
-
- public AndroidKeyStoreRSAPublicKey(String alias, int uid, byte[] x509EncodedForm, BigInteger modulus,
- BigInteger publicExponent) {
- super(alias, uid, KeyProperties.KEY_ALGORITHM_RSA, x509EncodedForm);
- mModulus = modulus;
- mPublicExponent = publicExponent;
- }
-
- public AndroidKeyStoreRSAPublicKey(String alias, int uid, RSAPublicKey info) {
- this(alias, uid, info.getEncoded(), info.getModulus(), info.getPublicExponent());
- if (!"X.509".equalsIgnoreCase(info.getFormat())) {
- throw new IllegalArgumentException(
- "Unsupported key export format: " + info.getFormat());
- }
- }
-
- @Override
- public BigInteger getModulus() {
- return mModulus;
- }
-
- @Override
- public BigInteger getPublicExponent() {
- return mPublicExponent;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
deleted file mode 100644
index ecfc97e..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreRSASignatureSpi.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import java.security.InvalidKeyException;
-import java.security.SignatureSpi;
-
-/**
- * Base class for {@link SignatureSpi} providing Android KeyStore backed RSA signatures.
- *
- * @hide
- */
-abstract class AndroidKeyStoreRSASignatureSpi extends AndroidKeyStoreSignatureSpiBase {
-
- abstract static class PKCS1Padding extends AndroidKeyStoreRSASignatureSpi {
- PKCS1Padding(int keymasterDigest) {
- super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForSign() {
- // No entropy required for this deterministic signature scheme.
- return 0;
- }
- }
-
- public static final class NONEWithPKCS1Padding extends PKCS1Padding {
- public NONEWithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_NONE);
- }
- }
-
- public static final class MD5WithPKCS1Padding extends PKCS1Padding {
- public MD5WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_MD5);
- }
- }
-
- public static final class SHA1WithPKCS1Padding extends PKCS1Padding {
- public SHA1WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public static final class SHA224WithPKCS1Padding extends PKCS1Padding {
- public SHA224WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public static final class SHA256WithPKCS1Padding extends PKCS1Padding {
- public SHA256WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public static final class SHA384WithPKCS1Padding extends PKCS1Padding {
- public SHA384WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public static final class SHA512WithPKCS1Padding extends PKCS1Padding {
- public SHA512WithPKCS1Padding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- abstract static class PSSPadding extends AndroidKeyStoreRSASignatureSpi {
- private static final int SALT_LENGTH_BYTES = 20;
-
- PSSPadding(int keymasterDigest) {
- super(keymasterDigest, KeymasterDefs.KM_PAD_RSA_PSS);
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForSign() {
- return SALT_LENGTH_BYTES;
- }
- }
-
- public static final class SHA1WithPSSPadding extends PSSPadding {
- public SHA1WithPSSPadding() {
- super(KeymasterDefs.KM_DIGEST_SHA1);
- }
- }
-
- public static final class SHA224WithPSSPadding extends PSSPadding {
- public SHA224WithPSSPadding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_224);
- }
- }
-
- public static final class SHA256WithPSSPadding extends PSSPadding {
- public SHA256WithPSSPadding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_256);
- }
- }
-
- public static final class SHA384WithPSSPadding extends PSSPadding {
- public SHA384WithPSSPadding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_384);
- }
- }
-
- public static final class SHA512WithPSSPadding extends PSSPadding {
- public SHA512WithPSSPadding() {
- super(KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- private final int mKeymasterDigest;
- private final int mKeymasterPadding;
-
- AndroidKeyStoreRSASignatureSpi(int keymasterDigest, int keymasterPadding) {
- mKeymasterDigest = keymasterDigest;
- mKeymasterPadding = keymasterPadding;
- }
-
- @Override
- protected final void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
- if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
- + ". Only" + KeyProperties.KEY_ALGORITHM_RSA + " supported");
- }
- super.initKey(key);
- }
-
- @Override
- protected final void resetAll() {
- super.resetAll();
- }
-
- @Override
- protected final void resetWhilePreservingInitState() {
- super.resetWhilePreservingInitState();
- }
-
- @Override
- protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
deleted file mode 100644
index b8e6af7..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import javax.crypto.SecretKey;
-
-/**
- * {@link SecretKey} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreSecretKey extends AndroidKeyStoreKey implements SecretKey {
-
- public AndroidKeyStoreSecretKey(String alias, int uid, String algorithm) {
- super(alias, uid, algorithm);
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
deleted file mode 100644
index d2678c7..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.Credentials;
-import android.security.GateKeeper;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterDefs;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.ProviderException;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactorySpi;
-import javax.crypto.spec.SecretKeySpec;
-
-/**
- * {@link SecretKeyFactorySpi} backed by Android Keystore.
- *
- * @hide
- */
-public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
-
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
- @Override
- protected KeySpec engineGetKeySpec(SecretKey key,
- @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
- if (keySpecClass == null) {
- throw new InvalidKeySpecException("keySpecClass == null");
- }
- if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
- ((key != null) ? key.getClass().getName() : "null"));
- }
- if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
- throw new InvalidKeySpecException(
- "Key material export of Android KeyStore keys is not supported");
- }
- if (!KeyInfo.class.equals(keySpecClass)) {
- throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
- }
- AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
- String keyAliasInKeystore = keystoreKey.getAlias();
- String entryAlias;
- if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
- entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
- } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
- // key has legacy prefix
- entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
- } else {
- throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
- }
-
- return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
- }
-
- static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
- int keyUid) {
- KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
- int errorCode = keyStore.getKeyCharacteristics(
- keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new ProviderException("Failed to obtain information about key."
- + " Keystore error: " + errorCode);
- }
-
- boolean insideSecureHardware;
- @KeyProperties.OriginEnum int origin;
- int keySize;
- @KeyProperties.PurposeEnum int purposes;
- String[] encryptionPaddings;
- String[] signaturePaddings;
- @KeyProperties.DigestEnum String[] digests;
- @KeyProperties.BlockModeEnum String[] blockModes;
- int keymasterSwEnforcedUserAuthenticators;
- int keymasterHwEnforcedUserAuthenticators;
- List<BigInteger> keymasterSecureUserIds;
- try {
- if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
- insideSecureHardware = true;
- origin = KeyProperties.Origin.fromKeymaster(
- keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
- } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
- insideSecureHardware = false;
- origin = KeyProperties.Origin.fromKeymaster(
- keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
- } else {
- throw new ProviderException("Key origin not available");
- }
- long keySizeUnsigned =
- keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
- if (keySizeUnsigned == -1) {
- throw new ProviderException("Key size not available");
- } else if (keySizeUnsigned > Integer.MAX_VALUE) {
- throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
- }
- keySize = (int) keySizeUnsigned;
- purposes = KeyProperties.Purpose.allFromKeymaster(
- keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
-
- List<String> encryptionPaddingsList = new ArrayList<String>();
- List<String> signaturePaddingsList = new ArrayList<String>();
- // Keymaster stores both types of paddings in the same array -- we split it into two.
- for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
- try {
- @KeyProperties.EncryptionPaddingEnum String jcaPadding =
- KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
- encryptionPaddingsList.add(jcaPadding);
- } catch (IllegalArgumentException e) {
- try {
- @KeyProperties.SignaturePaddingEnum String padding =
- KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
- signaturePaddingsList.add(padding);
- } catch (IllegalArgumentException e2) {
- throw new ProviderException(
- "Unsupported encryption padding: " + keymasterPadding);
- }
- }
-
- }
- encryptionPaddings =
- encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
- signaturePaddings =
- signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
-
- digests = KeyProperties.Digest.allFromKeymaster(
- keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
- blockModes = KeyProperties.BlockMode.allFromKeymaster(
- keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
- keymasterSwEnforcedUserAuthenticators =
- keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
- keymasterHwEnforcedUserAuthenticators =
- keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
- keymasterSecureUserIds =
- keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
- } catch (IllegalArgumentException e) {
- throw new ProviderException("Unsupported key characteristic", e);
- }
-
- Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
- Date keyValidityForOriginationEnd =
- keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
- Date keyValidityForConsumptionEnd =
- keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
- boolean userAuthenticationRequired =
- !keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- long userAuthenticationValidityDurationSeconds =
- keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, 0);
- if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
- throw new ProviderException("User authentication timeout validity too long: "
- + userAuthenticationValidityDurationSeconds + " seconds");
- }
- boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
- && (keymasterHwEnforcedUserAuthenticators != 0)
- && (keymasterSwEnforcedUserAuthenticators == 0);
- boolean userAuthenticationValidWhileOnBody =
- keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
- boolean trustedUserPresenceRequired =
- keyCharacteristics.hwEnforced.getBoolean(
- KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
-
- boolean invalidatedByBiometricEnrollment = false;
- if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC
- || keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) {
- // Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
- invalidatedByBiometricEnrollment = keymasterSecureUserIds != null
- && !keymasterSecureUserIds.isEmpty()
- && !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
- }
-
- boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
-
- return new KeyInfo(entryAlias,
- insideSecureHardware,
- origin,
- keySize,
- keyValidityStart,
- keyValidityForOriginationEnd,
- keyValidityForConsumptionEnd,
- purposes,
- encryptionPaddings,
- signaturePaddings,
- digests,
- blockModes,
- userAuthenticationRequired,
- (int) userAuthenticationValidityDurationSeconds,
- keymasterHwEnforcedUserAuthenticators,
- userAuthenticationRequirementEnforcedBySecureHardware,
- userAuthenticationValidWhileOnBody,
- trustedUserPresenceRequired,
- invalidatedByBiometricEnrollment,
- userConfirmationRequired,
- // Keystore 1.0 does not tell us the exact security level of the key
- // so we report an unknown but secure security level.
- insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
- : KeyProperties.SECURITY_LEVEL_SOFTWARE,
- KeyProperties.UNRESTRICTED_USAGE_COUNT);
- }
-
- private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
- try {
- return BigInteger.valueOf(GateKeeper.getSecureUserId());
- } catch (IllegalStateException e) {
- throw new ProviderException("Failed to get GateKeeper secure user ID", e);
- }
- }
-
- @Override
- protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
- throw new InvalidKeySpecException(
- "To generate secret key in Android Keystore, use KeyGenerator initialized with "
- + KeyGenParameterSpec.class.getName());
- }
-
- @Override
- protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
- if (key == null) {
- throw new InvalidKeyException("key == null");
- } else if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeyException(
- "To import a secret key into Android Keystore, use KeyStore.setEntry");
- }
-
- return key;
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
deleted file mode 100644
index da47b6b..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSignatureSpiBase.java
+++ /dev/null
@@ -1,431 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.CallSuper;
-import android.annotation.NonNull;
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-
-import libcore.util.EmptyArray;
-
-import java.nio.ByteBuffer;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.PrivateKey;
-import java.security.ProviderException;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.security.SignatureSpi;
-
-/**
- * Base class for {@link SignatureSpi} implementations of Android KeyStore backed ciphers.
- *
- * @hide
- */
-abstract class AndroidKeyStoreSignatureSpiBase extends SignatureSpi
- implements KeyStoreCryptoOperation {
- private final KeyStore mKeyStore;
-
- // Fields below are populated by SignatureSpi.engineInitSign/engineInitVerify and KeyStore.begin
- // and should be preserved after SignatureSpi.engineSign/engineVerify finishes.
- private boolean mSigning;
- private AndroidKeyStoreKey mKey;
-
- /**
- * Token referencing this operation inside keystore service. It is initialized by
- * {@code engineInitSign}/{@code engineInitVerify} and is invalidated when
- * {@code engineSign}/{@code engineVerify} succeeds and on some error conditions in between.
- */
- private IBinder mOperationToken;
- private long mOperationHandle;
- private KeyStoreCryptoOperationStreamer mMessageStreamer;
-
- /**
- * Encountered exception which could not be immediately thrown because it was encountered inside
- * a method that does not throw checked exception. This exception will be thrown from
- * {@code engineSign} or {@code engineVerify}. Once such an exception is encountered,
- * {@code engineUpdate} starts ignoring input data.
- */
- private Exception mCachedException;
-
- AndroidKeyStoreSignatureSpiBase() {
- mKeyStore = KeyStore.getInstance();
- }
-
- @Override
- protected final void engineInitSign(PrivateKey key) throws InvalidKeyException {
- engineInitSign(key, null);
- }
-
- @Override
- protected final void engineInitSign(PrivateKey privateKey, SecureRandom random)
- throws InvalidKeyException {
- resetAll();
-
- boolean success = false;
- try {
- if (privateKey == null) {
- throw new InvalidKeyException("Unsupported key: null");
- }
- AndroidKeyStoreKey keystoreKey;
- if (privateKey instanceof AndroidKeyStorePrivateKey) {
- keystoreKey = (AndroidKeyStoreKey) privateKey;
- } else {
- throw new InvalidKeyException("Unsupported private key type: " + privateKey);
- }
- mSigning = true;
- initKey(keystoreKey);
- appRandom = random;
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- @Override
- protected final void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
- resetAll();
-
- boolean success = false;
- try {
- if (publicKey == null) {
- throw new InvalidKeyException("Unsupported key: null");
- }
- AndroidKeyStoreKey keystoreKey;
- if (publicKey instanceof AndroidKeyStorePublicKey) {
- keystoreKey = (AndroidKeyStorePublicKey) publicKey;
- } else {
- throw new InvalidKeyException("Unsupported public key type: " + publicKey);
- }
- mSigning = false;
- initKey(keystoreKey);
- appRandom = null;
- ensureKeystoreOperationInitialized();
- success = true;
- } finally {
- if (!success) {
- resetAll();
- }
- }
- }
-
- /**
- * Configures this signature instance to use the provided key.
- *
- * @throws InvalidKeyException if the {@code key} is not suitable.
- */
- @CallSuper
- protected void initKey(AndroidKeyStoreKey key) throws InvalidKeyException {
- mKey = key;
- }
-
- /**
- * Resets this cipher to its pristine pre-init state. This must be equivalent to obtaining a new
- * cipher instance.
- *
- * <p>Subclasses storing additional state should override this method, reset the additional
- * state, and then chain to superclass.
- */
- @CallSuper
- protected void resetAll() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mOperationToken = null;
- mKeyStore.abort(operationToken);
- }
- mSigning = false;
- mKey = null;
- appRandom = null;
- mOperationToken = null;
- mOperationHandle = 0;
- mMessageStreamer = null;
- mCachedException = null;
- }
-
- /**
- * Resets this cipher while preserving the initialized state. This must be equivalent to
- * rolling back the cipher's state to just after the most recent {@code engineInit} completed
- * successfully.
- *
- * <p>Subclasses storing additional post-init state should override this method, reset the
- * additional state, and then chain to superclass.
- */
- @CallSuper
- protected void resetWhilePreservingInitState() {
- IBinder operationToken = mOperationToken;
- if (operationToken != null) {
- mOperationToken = null;
- mKeyStore.abort(operationToken);
- }
- mOperationHandle = 0;
- mMessageStreamer = null;
- mCachedException = null;
- }
-
- private void ensureKeystoreOperationInitialized() throws InvalidKeyException {
- if (mMessageStreamer != null) {
- return;
- }
- if (mCachedException != null) {
- return;
- }
- if (mKey == null) {
- throw new IllegalStateException("Not initialized");
- }
-
- KeymasterArguments keymasterInputArgs = new KeymasterArguments();
- addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
-
- OperationResult opResult = mKeyStore.begin(
- mKey.getAlias(),
- mSigning ? KeymasterDefs.KM_PURPOSE_SIGN : KeymasterDefs.KM_PURPOSE_VERIFY,
- true, // permit aborting this operation if keystore runs out of resources
- keymasterInputArgs,
- null, // no additional entropy for begin -- only finish might need some
- mKey.getUid());
- if (opResult == null) {
- throw new KeyStoreConnectException();
- }
-
- // Store operation token and handle regardless of the error code returned by KeyStore to
- // ensure that the operation gets aborted immediately if the code below throws an exception.
- mOperationToken = opResult.token;
- mOperationHandle = opResult.operationHandle;
-
- // If necessary, throw an exception due to KeyStore operation having failed.
- InvalidKeyException e = KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(
- mKeyStore, mKey, opResult.resultCode);
- if (e != null) {
- throw e;
- }
-
- if (mOperationToken == null) {
- throw new ProviderException("Keystore returned null operation token");
- }
- if (mOperationHandle == 0) {
- throw new ProviderException("Keystore returned invalid operation handle");
- }
-
- mMessageStreamer = createMainDataStreamer(mKeyStore, opResult.token);
- }
-
- /**
- * Creates a streamer which sends the message to be signed/verified into the provided KeyStore
- *
- * <p>This implementation returns a working streamer.
- */
- @NonNull
- protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
- KeyStore keyStore, IBinder operationToken) {
- return new KeyStoreCryptoOperationChunkedStreamer(
- new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- keyStore, operationToken));
- }
-
- @Override
- public final long getOperationHandle() {
- return mOperationHandle;
- }
-
- @Override
- protected final void engineUpdate(byte[] b, int off, int len) throws SignatureException {
- if (mCachedException != null) {
- throw new SignatureException(mCachedException);
- }
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new SignatureException(e);
- }
-
- if (len == 0) {
- return;
- }
-
- byte[] output;
- try {
- output = mMessageStreamer.update(b, off, len);
- } catch (KeyStoreException e) {
- throw new SignatureException(e);
- }
-
- if (output.length != 0) {
- throw new ProviderException(
- "Update operation unexpectedly produced output: " + output.length + " bytes");
- }
- }
-
- @Override
- protected final void engineUpdate(byte b) throws SignatureException {
- engineUpdate(new byte[] {b}, 0, 1);
- }
-
- @Override
- protected final void engineUpdate(ByteBuffer input) {
- byte[] b;
- int off;
- int len = input.remaining();
- if (input.hasArray()) {
- b = input.array();
- off = input.arrayOffset() + input.position();
- input.position(input.limit());
- } else {
- b = new byte[len];
- off = 0;
- input.get(b);
- }
-
- try {
- engineUpdate(b, off, len);
- } catch (SignatureException e) {
- mCachedException = e;
- }
- }
-
- @Override
- protected final int engineSign(byte[] out, int outOffset, int outLen)
- throws SignatureException {
- return super.engineSign(out, outOffset, outLen);
- }
-
- @Override
- protected final byte[] engineSign() throws SignatureException {
- if (mCachedException != null) {
- throw new SignatureException(mCachedException);
- }
-
- byte[] signature;
- try {
- ensureKeystoreOperationInitialized();
-
- byte[] additionalEntropy =
- KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
- appRandom, getAdditionalEntropyAmountForSign());
- signature = mMessageStreamer.doFinal(
- EmptyArray.BYTE, 0, 0,
- null, // no signature provided -- it'll be generated by this invocation
- additionalEntropy);
- } catch (InvalidKeyException | KeyStoreException e) {
- throw new SignatureException(e);
- }
-
- resetWhilePreservingInitState();
- return signature;
- }
-
- @Override
- protected final boolean engineVerify(byte[] signature) throws SignatureException {
- if (mCachedException != null) {
- throw new SignatureException(mCachedException);
- }
-
- try {
- ensureKeystoreOperationInitialized();
- } catch (InvalidKeyException e) {
- throw new SignatureException(e);
- }
-
- boolean verified;
- try {
- byte[] output = mMessageStreamer.doFinal(
- EmptyArray.BYTE, 0, 0,
- signature,
- null // no additional entropy needed -- verification is deterministic
- );
- if (output.length != 0) {
- throw new ProviderException(
- "Signature verification unexpected produced output: " + output.length
- + " bytes");
- }
- verified = true;
- } catch (KeyStoreException e) {
- switch (e.getErrorCode()) {
- case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
- verified = false;
- break;
- default:
- throw new SignatureException(e);
- }
- }
-
- resetWhilePreservingInitState();
- return verified;
- }
-
- @Override
- protected final boolean engineVerify(byte[] sigBytes, int offset, int len)
- throws SignatureException {
- return engineVerify(ArrayUtils.subarray(sigBytes, offset, len));
- }
-
- @Deprecated
- @Override
- protected final Object engineGetParameter(String param) throws InvalidParameterException {
- throw new InvalidParameterException();
- }
-
- @Deprecated
- @Override
- protected final void engineSetParameter(String param, Object value)
- throws InvalidParameterException {
- throw new InvalidParameterException();
- }
-
- protected final KeyStore getKeyStore() {
- return mKeyStore;
- }
-
- /**
- * Returns {@code true} if this signature is initialized for signing, {@code false} if this
- * signature is initialized for verification.
- */
- protected final boolean isSigning() {
- return mSigning;
- }
-
- // The methods below need to be implemented by subclasses.
-
- /**
- * Returns the amount of additional entropy (in bytes) to be provided to the KeyStore's
- * {@code finish} operation when generating a signature.
- *
- * <p>This value should match (or exceed) the amount of Shannon entropy of the produced
- * signature assuming the key and the message are known. For example, for ECDSA signature this
- * should be the size of {@code R}, whereas for the RSA signature with PKCS#1 padding this
- * should be {@code 0}.
- */
- protected abstract int getAdditionalEntropyAmountForSign();
-
- /**
- * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
- *
- * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
- * parameters.
- */
- protected abstract void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs);
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
deleted file mode 100644
index 51c4252..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ /dev/null
@@ -1,1112 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.Credentials;
-import android.security.GateKeeper;
-import android.security.KeyStore;
-import android.security.KeyStoreParameter;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keystore.KeyPermanentlyInvalidatedException;
-import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyProtection;
-import android.security.keystore.SecureKeyImportUnavailableException;
-import android.security.keystore.WrappedKeyEntry;
-import android.util.Log;
-
-import libcore.util.EmptyArray;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.Key;
-import java.security.KeyStore.Entry;
-import java.security.KeyStore.LoadStoreParameter;
-import java.security.KeyStore.PrivateKeyEntry;
-import java.security.KeyStore.ProtectionParameter;
-import java.security.KeyStore.SecretKeyEntry;
-import java.security.KeyStoreException;
-import java.security.KeyStoreSpi;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.ProviderException;
-import java.security.PublicKey;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import javax.crypto.SecretKey;
-
-/**
- * A java.security.KeyStore interface for the Android KeyStore. An instance of
- * it can be created via the {@link java.security.KeyStore#getInstance(String)
- * KeyStore.getInstance("AndroidKeyStore")} interface. This returns a
- * java.security.KeyStore backed by this "AndroidKeyStore" implementation.
- * <p>
- * This is built on top of Android's keystore daemon. The convention of alias
- * use is:
- * <p>
- * PrivateKeyEntry will have a Credentials.USER_PRIVATE_KEY as the private key,
- * Credentials.USER_CERTIFICATE as the first certificate in the chain (the one
- * that corresponds to the private key), and then a Credentials.CA_CERTIFICATE
- * entry which will have the rest of the chain concatenated in BER format.
- * <p>
- * TrustedCertificateEntry will just have a Credentials.CA_CERTIFICATE entry
- * with a single certificate.
- *
- * @hide
- */
-public class AndroidKeyStoreSpi extends KeyStoreSpi {
- public static final String NAME = "AndroidKeyStore";
-
- private KeyStore mKeyStore;
- private int mUid = KeyStore.UID_SELF;
-
- @Override
- public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
- UnrecoverableKeyException {
- String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- AndroidKeyStoreKey key;
- if (!mKeyStore.contains(userKeyAlias, mUid)) {
- // try legacy prefix for backward compatibility
- userKeyAlias = Credentials.USER_SECRET_KEY + alias;
- if (!mKeyStore.contains(userKeyAlias, mUid)) return null;
- }
- try {
- key = AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore,
- userKeyAlias,
- mUid);
- } catch (KeyPermanentlyInvalidatedException e) {
- throw new UnrecoverableKeyException(e.getMessage());
- }
- return key;
- }
-
- @Override
- public Certificate[] engineGetCertificateChain(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- final X509Certificate leaf = (X509Certificate) engineGetCertificate(alias);
- if (leaf == null) {
- return null;
- }
-
- final Certificate[] caList;
-
- // Suppress the key not found warning for this call. It seems that this error is exclusively
- // being thrown when there is a self signed certificate chain, so when the keystore service
- // attempts to query for the CA details, it obviously fails to find them and returns a
- // key not found exception. This is WAI, and throwing a stack trace here can be very
- // misleading since the trace is not clear.
- final byte[] caBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias,
- mUid,
- true /* suppressKeyNotFoundWarning */);
- if (caBytes != null) {
- final Collection<X509Certificate> caChain = toCertificates(caBytes);
-
- caList = new Certificate[caChain.size() + 1];
-
- final Iterator<X509Certificate> it = caChain.iterator();
- int i = 1;
- while (it.hasNext()) {
- caList[i++] = it.next();
- }
- } else {
- caList = new Certificate[1];
- }
-
- caList[0] = leaf;
-
- return caList;
- }
-
- @Override
- public Certificate engineGetCertificate(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- byte[] encodedCert = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
- if (encodedCert != null) {
- return getCertificateForPrivateKeyEntry(alias, encodedCert);
- }
-
- encodedCert = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
- if (encodedCert != null) {
- return getCertificateForTrustedCertificateEntry(encodedCert);
- }
-
- // This entry/alias does not contain a certificate.
- return null;
- }
-
- private Certificate getCertificateForTrustedCertificateEntry(byte[] encodedCert) {
- // For this certificate there shouldn't be a private key in this KeyStore entry. Thus,
- // there's no need to wrap this certificate as opposed to the certificate associated with
- // a private key entry.
- return toCertificate(encodedCert);
- }
-
- private Certificate getCertificateForPrivateKeyEntry(String alias, byte[] encodedCert) {
- // All crypto algorithms offered by Android Keystore for its private keys must also
- // be offered for the corresponding public keys stored in the Android Keystore. The
- // complication is that the underlying keystore service operates only on full key pairs,
- // rather than just public keys or private keys. As a result, Android Keystore-backed
- // crypto can only be offered for public keys for which keystore contains the
- // corresponding private key. This is not the case for certificate-only entries (e.g.,
- // trusted certificates).
- //
- // getCertificate().getPublicKey() is the only way to obtain the public key
- // corresponding to the private key stored in the KeyStore. Thus, we need to make sure
- // that the returned public key points to the underlying key pair / private key
- // when available.
-
- X509Certificate cert = toCertificate(encodedCert);
- if (cert == null) {
- // Failed to parse the certificate.
- return null;
- }
-
- String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
- if (mKeyStore.contains(privateKeyAlias, mUid)) {
- // As expected, keystore contains the private key corresponding to this public key. Wrap
- // the certificate so that its getPublicKey method returns an Android Keystore
- // PublicKey. This key will delegate crypto operations involving this public key to
- // Android Keystore when higher-priority providers do not offer these crypto
- // operations for this key.
- return wrapIntoKeyStoreCertificate(privateKeyAlias, mUid, cert);
- } else {
- // This KeyStore entry/alias is supposed to contain the private key corresponding to
- // the public key in this certificate, but it does not for some reason. It's probably a
- // bug. Let other providers handle crypto operations involving the public key returned
- // by this certificate's getPublicKey.
- return cert;
- }
- }
-
- /**
- * Wraps the provided cerificate into {@link KeyStoreX509Certificate} so that the public key
- * returned by the certificate contains information about the alias of the private key in
- * keystore. This is needed so that Android Keystore crypto operations using public keys can
- * find out which key alias to use. These operations cannot work without an alias.
- */
- private static KeyStoreX509Certificate wrapIntoKeyStoreCertificate(
- String privateKeyAlias, int uid, X509Certificate certificate) {
- return (certificate != null)
- ? new KeyStoreX509Certificate(privateKeyAlias, uid, certificate) : null;
- }
-
- private static X509Certificate toCertificate(byte[] bytes) {
- try {
- final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- return (X509Certificate) certFactory.generateCertificate(
- new ByteArrayInputStream(bytes));
- } catch (CertificateException e) {
- Log.w(NAME, "Couldn't parse certificate in keystore", e);
- return null;
- }
- }
-
- @SuppressWarnings("unchecked")
- private static Collection<X509Certificate> toCertificates(byte[] bytes) {
- try {
- final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- return (Collection<X509Certificate>) certFactory.generateCertificates(
- new ByteArrayInputStream(bytes));
- } catch (CertificateException e) {
- Log.w(NAME, "Couldn't parse certificates in keystore", e);
- return new ArrayList<X509Certificate>();
- }
- }
-
- private Date getModificationDate(String alias) {
- final long epochMillis = mKeyStore.getmtime(alias, mUid);
- if (epochMillis == -1L) {
- return null;
- }
-
- return new Date(epochMillis);
- }
-
- @Override
- public Date engineGetCreationDate(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- Date d = getModificationDate(Credentials.USER_PRIVATE_KEY + alias);
- if (d != null) {
- return d;
- }
-
- d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
- if (d != null) {
- return d;
- }
-
- d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
- if (d != null) {
- return d;
- }
-
- return getModificationDate(Credentials.CA_CERTIFICATE + alias);
- }
-
- @Override
- public void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain)
- throws KeyStoreException {
- if ((password != null) && (password.length > 0)) {
- throw new KeyStoreException("entries cannot be protected with passwords");
- }
-
- if (key instanceof PrivateKey) {
- setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
- } else if (key instanceof SecretKey) {
- setSecretKeyEntry(alias, (SecretKey) key, null);
- } else {
- throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
- }
- }
-
- private static KeyProtection getLegacyKeyProtectionParameter(PrivateKey key)
- throws KeyStoreException {
- String keyAlgorithm = key.getAlgorithm();
- KeyProtection.Builder specBuilder;
- if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder =
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- // MD5 was never offered for Android Keystore for ECDSA.
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
- specBuilder =
- new KeyProtection.Builder(
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT
- | KeyProperties.PURPOSE_SIGN
- | KeyProperties.PURPOSE_VERIFY);
- // Authorized to be used with any digest (including no digest).
- specBuilder.setDigests(
- KeyProperties.DIGEST_NONE,
- KeyProperties.DIGEST_MD5,
- KeyProperties.DIGEST_SHA1,
- KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256,
- KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512);
- // Authorized to be used with any encryption and signature padding
- // schemes (including no padding).
- specBuilder.setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE,
- KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
- KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
- specBuilder.setSignaturePaddings(
- KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
- KeyProperties.SIGNATURE_PADDING_RSA_PSS);
- // Disable randomized encryption requirement to support encryption
- // padding NONE above.
- specBuilder.setRandomizedEncryptionRequired(false);
- } else {
- throw new KeyStoreException("Unsupported key algorithm: " + keyAlgorithm);
- }
- specBuilder.setUserAuthenticationRequired(false);
-
- return specBuilder.build();
- }
-
- private void setPrivateKeyEntry(String alias, PrivateKey key, Certificate[] chain,
- java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
- int flags = 0;
- KeyProtection spec;
- if (param == null) {
- spec = getLegacyKeyProtectionParameter(key);
- } else if (param instanceof KeyStoreParameter) {
- spec = getLegacyKeyProtectionParameter(key);
- KeyStoreParameter legacySpec = (KeyStoreParameter) param;
- if (legacySpec.isEncryptionRequired()) {
- flags = KeyStore.FLAG_ENCRYPTED;
- }
- } else if (param instanceof KeyProtection) {
- spec = (KeyProtection) param;
- if (spec.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
- }
-
- if (spec.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
- }
- } else {
- throw new KeyStoreException(
- "Unsupported protection parameter class:" + param.getClass().getName()
- + ". Supported: " + KeyProtection.class.getName() + ", "
- + KeyStoreParameter.class.getName());
- }
-
- // Make sure the chain exists since this is a PrivateKey
- if ((chain == null) || (chain.length == 0)) {
- throw new KeyStoreException("Must supply at least one Certificate with PrivateKey");
- }
-
- // Do chain type checking.
- X509Certificate[] x509chain = new X509Certificate[chain.length];
- for (int i = 0; i < chain.length; i++) {
- if (!"X.509".equals(chain[i].getType())) {
- throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
- + i);
- }
-
- if (!(chain[i] instanceof X509Certificate)) {
- throw new KeyStoreException("Certificates must be in X.509 format: invalid cert #"
- + i);
- }
-
- x509chain[i] = (X509Certificate) chain[i];
- }
-
- final byte[] userCertBytes;
- try {
- userCertBytes = x509chain[0].getEncoded();
- } catch (CertificateEncodingException e) {
- throw new KeyStoreException("Failed to encode certificate #0", e);
- }
-
- /*
- * If we have a chain, store it in the CA certificate slot for this
- * alias as concatenated DER-encoded certificates. These can be
- * deserialized by {@link CertificateFactory#generateCertificates}.
- */
- final byte[] chainBytes;
- if (chain.length > 1) {
- /*
- * The chain is passed in as {user_cert, ca_cert_1, ca_cert_2, ...}
- * so we only need the certificates starting at index 1.
- */
- final byte[][] certsBytes = new byte[x509chain.length - 1][];
- int totalCertLength = 0;
- for (int i = 0; i < certsBytes.length; i++) {
- try {
- certsBytes[i] = x509chain[i + 1].getEncoded();
- totalCertLength += certsBytes[i].length;
- } catch (CertificateEncodingException e) {
- throw new KeyStoreException("Failed to encode certificate #" + i, e);
- }
- }
-
- /*
- * Serialize this into one byte array so we can later call
- * CertificateFactory#generateCertificates to recover them.
- */
- chainBytes = new byte[totalCertLength];
- int outputOffset = 0;
- for (int i = 0; i < certsBytes.length; i++) {
- final int certLength = certsBytes[i].length;
- System.arraycopy(certsBytes[i], 0, chainBytes, outputOffset, certLength);
- outputOffset += certLength;
- certsBytes[i] = null;
- }
- } else {
- chainBytes = null;
- }
-
- final String pkeyAlias;
- if (key instanceof AndroidKeyStorePrivateKey) {
- pkeyAlias = ((AndroidKeyStoreKey) key).getAlias();
- } else {
- pkeyAlias = null;
- }
-
- byte[] pkcs8EncodedPrivateKeyBytes;
- KeymasterArguments importArgs;
- final boolean shouldReplacePrivateKey;
- if (pkeyAlias != null && pkeyAlias.startsWith(Credentials.USER_PRIVATE_KEY)) {
- final String keySubalias = pkeyAlias.substring(Credentials.USER_PRIVATE_KEY.length());
- if (!alias.equals(keySubalias)) {
- throw new KeyStoreException("Can only replace keys with same alias: " + alias
- + " != " + keySubalias);
- }
- shouldReplacePrivateKey = false;
- importArgs = null;
- pkcs8EncodedPrivateKeyBytes = null;
- } else {
- shouldReplacePrivateKey = true;
- // Make sure the PrivateKey format is the one we support.
- final String keyFormat = key.getFormat();
- if ((keyFormat == null) || (!"PKCS#8".equals(keyFormat))) {
- throw new KeyStoreException(
- "Unsupported private key export format: " + keyFormat
- + ". Only private keys which export their key material in PKCS#8 format are"
- + " supported.");
- }
-
- // Make sure we can actually encode the key.
- pkcs8EncodedPrivateKeyBytes = key.getEncoded();
- if (pkcs8EncodedPrivateKeyBytes == null) {
- throw new KeyStoreException("Private key did not export any key material");
- }
-
- importArgs = new KeymasterArguments();
- try {
- importArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
- KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(
- key.getAlgorithm()));
- @KeyProperties.PurposeEnum int purposes = spec.getPurposes();
- importArgs.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
- KeyProperties.Purpose.allToKeymaster(purposes));
- if (spec.isDigestsSpecified()) {
- importArgs.addEnums(KeymasterDefs.KM_TAG_DIGEST,
- KeyProperties.Digest.allToKeymaster(spec.getDigests()));
- }
-
- importArgs.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE,
- KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()));
- int[] keymasterEncryptionPaddings =
- KeyProperties.EncryptionPadding.allToKeymaster(
- spec.getEncryptionPaddings());
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (spec.isRandomizedEncryptionRequired())) {
- for (int keymasterPadding : keymasterEncryptionPaddings) {
- if (!KeymasterUtils
- .isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
- keymasterPadding)) {
- throw new KeyStoreException(
- "Randomized encryption (IND-CPA) required but is violated by"
- + " encryption padding mode: "
- + KeyProperties.EncryptionPadding.fromKeymaster(
- keymasterPadding)
- + ". See KeyProtection documentation.");
- }
- }
- }
- importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
- importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
- KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
- KeymasterUtils.addUserAuthArgs(importArgs, spec);
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
- spec.getKeyValidityStart());
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- spec.getKeyValidityForOriginationEnd());
- importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- spec.getKeyValidityForConsumptionEnd());
- } catch (IllegalArgumentException | IllegalStateException e) {
- throw new KeyStoreException(e);
- }
- }
-
-
- boolean success = false;
- try {
- // Store the private key, if necessary
- if (shouldReplacePrivateKey) {
- // Delete the stored private key and any related entries before importing the
- // provided key
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
- KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
- int errorCode = mKeyStore.importKey(
- Credentials.USER_PRIVATE_KEY + alias,
- importArgs,
- KeymasterDefs.KM_KEY_FORMAT_PKCS8,
- pkcs8EncodedPrivateKeyBytes,
- mUid,
- flags,
- resultingKeyCharacteristics);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store private key",
- KeyStore.getKeyStoreException(errorCode));
- }
- } else {
- // Keep the stored private key around -- delete all other entry types
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
- }
-
- // Store the leaf certificate
- int errorCode = mKeyStore.insert(Credentials.USER_CERTIFICATE + alias, userCertBytes,
- mUid, flags);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store certificate #0",
- KeyStore.getKeyStoreException(errorCode));
- }
-
- // Store the certificate chain
- errorCode = mKeyStore.insert(Credentials.CA_CERTIFICATE + alias, chainBytes,
- mUid, flags);
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to store certificate chain",
- KeyStore.getKeyStoreException(errorCode));
- }
- success = true;
- } finally {
- if (!success) {
- if (shouldReplacePrivateKey) {
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
- } else {
- Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
- Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
- }
- }
- }
- }
-
- private void setSecretKeyEntry(String entryAlias, SecretKey key,
- java.security.KeyStore.ProtectionParameter param)
- throws KeyStoreException {
- if ((param != null) && (!(param instanceof KeyProtection))) {
- throw new KeyStoreException(
- "Unsupported protection parameter class: " + param.getClass().getName()
- + ". Supported: " + KeyProtection.class.getName());
- }
- KeyProtection params = (KeyProtection) param;
-
- if (key instanceof AndroidKeyStoreSecretKey) {
- // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
- // overwrite its own entry.
- String keyAliasInKeystore = ((AndroidKeyStoreSecretKey) key).getAlias();
- if (keyAliasInKeystore == null) {
- throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
- }
- String keyAliasPrefix = Credentials.USER_PRIVATE_KEY;
- if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
- // try legacy prefix
- keyAliasPrefix = Credentials.USER_SECRET_KEY;
- if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
- throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
- + keyAliasInKeystore);
- }
- }
- String keyEntryAlias =
- keyAliasInKeystore.substring(keyAliasPrefix.length());
- if (!entryAlias.equals(keyEntryAlias)) {
- throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
- + " alias: " + entryAlias + " != " + keyEntryAlias);
- }
- // This is the entry where this key is already stored. No need to do anything.
- if (params != null) {
- throw new KeyStoreException("Modifying KeyStore-backed key using protection"
- + " parameters not supported");
- }
- return;
- }
-
- if (params == null) {
- throw new KeyStoreException(
- "Protection parameters must be specified when importing a symmetric key");
- }
-
- // Not a KeyStore-backed secret key -- import its key material into keystore.
- String keyExportFormat = key.getFormat();
- if (keyExportFormat == null) {
- throw new KeyStoreException(
- "Only secret keys that export their key material are supported");
- } else if (!"RAW".equals(keyExportFormat)) {
- throw new KeyStoreException(
- "Unsupported secret key material export format: " + keyExportFormat);
- }
- byte[] keyMaterial = key.getEncoded();
- if (keyMaterial == null) {
- throw new KeyStoreException("Key did not export its key material despite supporting"
- + " RAW format export");
- }
-
- KeymasterArguments args = new KeymasterArguments();
- try {
- int keymasterAlgorithm =
- KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);
-
- int[] keymasterDigests;
- if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
- // JCA HMAC key algorithm implies a digest (e.g., HmacSHA256 key algorithm
- // implies SHA-256 digest). Because keymaster HMAC key is authorized only for one
- // digest, we don't let import parameters override the digest implied by the key.
- // If the parameters specify digests at all, they must specify only one digest, the
- // only implied by key algorithm.
- int keymasterImpliedDigest =
- KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
- if (keymasterImpliedDigest == -1) {
- throw new ProviderException(
- "HMAC key algorithm digest unknown for key algorithm "
- + key.getAlgorithm());
- }
- keymasterDigests = new int[] {keymasterImpliedDigest};
- if (params.isDigestsSpecified()) {
- // Digest(s) explicitly specified in params -- check that the list consists of
- // exactly one digest, the one implied by key algorithm.
- int[] keymasterDigestsFromParams =
- KeyProperties.Digest.allToKeymaster(params.getDigests());
- if ((keymasterDigestsFromParams.length != 1)
- || (keymasterDigestsFromParams[0] != keymasterImpliedDigest)) {
- throw new KeyStoreException(
- "Unsupported digests specification: "
- + Arrays.asList(params.getDigests()) + ". Only "
- + KeyProperties.Digest.fromKeymaster(keymasterImpliedDigest)
- + " supported for HMAC key algorithm " + key.getAlgorithm());
- }
- }
- } else {
- // Key algorithm does not imply a digest.
- if (params.isDigestsSpecified()) {
- keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
- } else {
- keymasterDigests = EmptyArray.INT;
- }
- }
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
-
- @KeyProperties.PurposeEnum int purposes = params.getPurposes();
- int[] keymasterBlockModes =
- KeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (params.isRandomizedEncryptionRequired())) {
- for (int keymasterBlockMode : keymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
- keymasterBlockMode)) {
- throw new KeyStoreException(
- "Randomized encryption (IND-CPA) required but may be violated by"
- + " block mode: "
- + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
- + ". See KeyProtection documentation.");
- }
- }
- }
- args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
- KeyProperties.Purpose.allToKeymaster(purposes));
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- if (params.getSignaturePaddings().length > 0) {
- throw new KeyStoreException("Signature paddings not supported for symmetric keys");
- }
- int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
- params.getEncryptionPaddings());
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
- KeymasterUtils.addUserAuthArgs(args, params);
- KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
- args,
- keymasterAlgorithm,
- keymasterBlockModes,
- keymasterDigests);
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
- params.getKeyValidityStart());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
- params.getKeyValidityForOriginationEnd());
- args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
- params.getKeyValidityForConsumptionEnd());
-
- if (((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)
- && (!params.isRandomizedEncryptionRequired())) {
- // Permit caller-provided IV when encrypting with this key
- args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
- }
- } catch (IllegalArgumentException | IllegalStateException e) {
- throw new KeyStoreException(e);
- }
- int flags = 0;
- if (params.isCriticalToDeviceEncryption()) {
- flags |= KeyStore.FLAG_CRITICAL_TO_DEVICE_ENCRYPTION;
- }
- if (params.isStrongBoxBacked()) {
- flags |= KeyStore.FLAG_STRONGBOX;
- }
-
- Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
- String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias;
- int errorCode = mKeyStore.importKey(
- keyAliasInKeystore,
- args,
- KeymasterDefs.KM_KEY_FORMAT_RAW,
- keyMaterial,
- mUid,
- flags,
- new KeyCharacteristics());
- if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to import secret key. Keystore error code: "
- + errorCode);
- }
- }
-
- private void setWrappedKeyEntry(String alias, WrappedKeyEntry entry,
- java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
- if (param != null) {
- throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
- }
-
- byte[] maskingKey = new byte[32];
-
-
- KeymasterArguments args = new KeymasterArguments();
- String[] parts = entry.getTransformation().split("/");
-
- String algorithm = parts[0];
- if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- } else if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
- args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
- }
-
- if (parts.length > 1) {
- String mode = parts[1];
- if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
- } else if (KeyProperties.BLOCK_MODE_CBC.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CBC);
- } else if (KeyProperties.BLOCK_MODE_CTR.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
- } else if (KeyProperties.BLOCK_MODE_GCM.equalsIgnoreCase(mode)) {
- args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
- }
- }
-
- if (parts.length > 2) {
- String padding = parts[2];
- if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
- // Noop
- } else if (KeyProperties.ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING,
- KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
- } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
- args.addEnums(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP);
- }
- }
-
- KeyGenParameterSpec spec = (KeyGenParameterSpec) entry.getAlgorithmParameterSpec();
- if (spec.isDigestsSpecified()) {
- String digest = spec.getDigests()[0];
- if (KeyProperties.DIGEST_NONE.equalsIgnoreCase(digest)) {
- // Noop
- } else if (KeyProperties.DIGEST_MD5.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5);
- } else if (KeyProperties.DIGEST_SHA1.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1);
- } else if (KeyProperties.DIGEST_SHA224.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224);
- } else if (KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256);
- } else if (KeyProperties.DIGEST_SHA384.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384);
- } else if (KeyProperties.DIGEST_SHA512.equalsIgnoreCase(digest)) {
- args.addEnums(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512);
- }
- }
-
- int errorCode = mKeyStore.importWrappedKey(
- Credentials.USER_PRIVATE_KEY + alias,
- entry.getWrappedKeyBytes(),
- Credentials.USER_PRIVATE_KEY + entry.getWrappingKeyAlias(),
- maskingKey,
- args,
- GateKeeper.getSecureUserId(),
- 0, // FIXME fingerprint id?
- mUid,
- new KeyCharacteristics());
- if (errorCode == KeymasterDefs.KM_ERROR_UNIMPLEMENTED) {
- throw new SecureKeyImportUnavailableException("Could not import wrapped key");
- } else if (errorCode != KeyStore.NO_ERROR) {
- throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
- + errorCode);
- }
- }
-
- @Override
- public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
- throws KeyStoreException {
- throw new KeyStoreException("Operation not supported because key encoding is unknown");
- }
-
- @Override
- public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException {
- if (isKeyEntry(alias)) {
- throw new KeyStoreException("Entry exists and is not a trusted certificate");
- }
-
- // We can't set something to null.
- if (cert == null) {
- throw new NullPointerException("cert == null");
- }
-
- final byte[] encoded;
- try {
- encoded = cert.getEncoded();
- } catch (CertificateEncodingException e) {
- throw new KeyStoreException(e);
- }
-
- if (!mKeyStore.put(Credentials.CA_CERTIFICATE + alias, encoded, mUid, KeyStore.FLAG_NONE)) {
- throw new KeyStoreException("Couldn't insert certificate; is KeyStore initialized?");
- }
- }
-
- @Override
- public void engineDeleteEntry(String alias) throws KeyStoreException {
- if (!Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid)) {
- throw new KeyStoreException("Failed to delete entry: " + alias);
- }
- }
-
- private Set<String> getUniqueAliases() {
- final String[] rawAliases = mKeyStore.list("", mUid);
- if (rawAliases == null) {
- return new HashSet<String>();
- }
-
- final Set<String> aliases = new HashSet<String>(rawAliases.length);
- for (String alias : rawAliases) {
- final int idx = alias.indexOf('_');
- if ((idx == -1) || (alias.length() <= idx)) {
- Log.e(NAME, "invalid alias: " + alias);
- continue;
- }
-
- aliases.add(new String(alias.substring(idx + 1)));
- }
-
- return aliases;
- }
-
- @Override
- public Enumeration<String> engineAliases() {
- return Collections.enumeration(getUniqueAliases());
- }
-
- @Override
- public boolean engineContainsAlias(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid)
- || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid)
- || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias, mUid)
- || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
- }
-
- @Override
- public int engineSize() {
- return getUniqueAliases().size();
- }
-
- @Override
- public boolean engineIsKeyEntry(String alias) {
- return isKeyEntry(alias);
- }
-
- private boolean isKeyEntry(String alias) {
- return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) ||
- mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
- }
-
-
- private boolean isCertificateEntry(String alias) {
- if (alias == null) {
- throw new NullPointerException("alias == null");
- }
-
- return mKeyStore.contains(Credentials.CA_CERTIFICATE + alias, mUid);
- }
-
- @Override
- public boolean engineIsCertificateEntry(String alias) {
- return !isKeyEntry(alias) && isCertificateEntry(alias);
- }
-
- @Override
- public String engineGetCertificateAlias(Certificate cert) {
- if (cert == null) {
- return null;
- }
- if (!"X.509".equalsIgnoreCase(cert.getType())) {
- // Only X.509 certificates supported
- return null;
- }
- byte[] targetCertBytes;
- try {
- targetCertBytes = cert.getEncoded();
- } catch (CertificateEncodingException e) {
- return null;
- }
- if (targetCertBytes == null) {
- return null;
- }
-
- final Set<String> nonCaEntries = new HashSet<String>();
-
- /*
- * First scan the PrivateKeyEntry types. The KeyStoreSpi documentation
- * says to only compare the first certificate in the chain which is
- * equivalent to the USER_CERTIFICATE prefix for the Android keystore
- * convention.
- */
- final String[] certAliases = mKeyStore.list(Credentials.USER_CERTIFICATE, mUid);
- if (certAliases != null) {
- for (String alias : certAliases) {
- final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias, mUid);
- if (certBytes == null) {
- continue;
- }
-
- nonCaEntries.add(alias);
-
- if (Arrays.equals(certBytes, targetCertBytes)) {
- return alias;
- }
- }
- }
-
- /*
- * Look at all the TrustedCertificateEntry types. Skip all the
- * PrivateKeyEntry we looked at above.
- */
- final String[] caAliases = mKeyStore.list(Credentials.CA_CERTIFICATE, mUid);
- if (certAliases != null) {
- for (String alias : caAliases) {
- if (nonCaEntries.contains(alias)) {
- continue;
- }
-
- final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias, mUid);
- if (certBytes == null) {
- continue;
- }
-
- if (Arrays.equals(certBytes, targetCertBytes)) {
- return alias;
- }
- }
- }
-
- return null;
- }
-
- @Override
- public void engineStore(OutputStream stream, char[] password) throws IOException,
- NoSuchAlgorithmException, CertificateException {
- throw new UnsupportedOperationException("Can not serialize AndroidKeyStore to OutputStream");
- }
-
- @Override
- public void engineLoad(InputStream stream, char[] password) throws IOException,
- NoSuchAlgorithmException, CertificateException {
- if (stream != null) {
- throw new IllegalArgumentException("InputStream not supported");
- }
-
- if (password != null) {
- throw new IllegalArgumentException("password not supported");
- }
-
- // Unfortunate name collision.
- mKeyStore = KeyStore.getInstance();
- mUid = KeyStore.UID_SELF;
- }
-
- @Override
- public void engineLoad(LoadStoreParameter param) throws IOException,
- NoSuchAlgorithmException, CertificateException {
- int uid = KeyStore.UID_SELF;
- if (param != null) {
- if (param instanceof AndroidKeyStoreLoadStoreParameter) {
- uid = ((AndroidKeyStoreLoadStoreParameter) param).getUid();
- } else {
- throw new IllegalArgumentException(
- "Unsupported param type: " + param.getClass());
- }
- }
- mKeyStore = KeyStore.getInstance();
- mUid = uid;
- }
-
- @Override
- public void engineSetEntry(String alias, Entry entry, ProtectionParameter param)
- throws KeyStoreException {
- if (entry == null) {
- throw new KeyStoreException("entry == null");
- }
-
- Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
-
- if (entry instanceof java.security.KeyStore.TrustedCertificateEntry) {
- java.security.KeyStore.TrustedCertificateEntry trE =
- (java.security.KeyStore.TrustedCertificateEntry) entry;
- engineSetCertificateEntry(alias, trE.getTrustedCertificate());
- return;
- }
-
- if (entry instanceof PrivateKeyEntry) {
- PrivateKeyEntry prE = (PrivateKeyEntry) entry;
- setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(), param);
- } else if (entry instanceof SecretKeyEntry) {
- SecretKeyEntry secE = (SecretKeyEntry) entry;
- setSecretKeyEntry(alias, secE.getSecretKey(), param);
- } else if (entry instanceof WrappedKeyEntry) {
- WrappedKeyEntry wke = (WrappedKeyEntry) entry;
- setWrappedKeyEntry(alias, wke, param);
- } else {
- throw new KeyStoreException(
- "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
- + "; was " + entry);
- }
- }
-
- /**
- * {@link X509Certificate} which returns {@link AndroidKeyStorePublicKey} from
- * {@link #getPublicKey()}. This is so that crypto operations on these public keys contain
- * can find out which keystore private key entry to use. This is needed so that Android Keystore
- * crypto operations using public keys can find out which key alias to use. These operations
- * require an alias.
- */
- static class KeyStoreX509Certificate extends DelegatingX509Certificate {
- private final String mPrivateKeyAlias;
- private final int mPrivateKeyUid;
- KeyStoreX509Certificate(String privateKeyAlias, int privateKeyUid,
- X509Certificate delegate) {
- super(delegate);
- mPrivateKeyAlias = privateKeyAlias;
- mPrivateKeyUid = privateKeyUid;
- }
-
- @Override
- public PublicKey getPublicKey() {
- PublicKey original = super.getPublicKey();
- return AndroidKeyStoreProvider.getAndroidKeyStorePublicKey(
- mPrivateKeyAlias, mPrivateKeyUid,
- original.getAlgorithm(), original.getEncoded());
- }
- }
-}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
deleted file mode 100644
index 1f1d36f..0000000
--- a/keystore/java/android/security/keystore/AndroidKeyStoreUnauthenticatedAESCipherSpi.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.NoSuchAlgorithmException;
-import java.security.ProviderException;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidParameterSpecException;
-import java.util.Arrays;
-
-import javax.crypto.CipherSpi;
-import javax.crypto.spec.IvParameterSpec;
-
-/**
- * Base class for Android Keystore unauthenticated AES {@link CipherSpi} implementations.
- *
- * @hide
- */
-class AndroidKeyStoreUnauthenticatedAESCipherSpi extends AndroidKeyStoreCipherSpiBase {
-
- abstract static class ECB extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
- protected ECB(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
- }
-
- public static class NoPadding extends ECB {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
- }
-
- public static class PKCS7Padding extends ECB {
- public PKCS7Padding() {
- super(KeymasterDefs.KM_PAD_PKCS7);
- }
- }
- }
-
- abstract static class CBC extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
- protected CBC(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
- }
-
- public static class NoPadding extends CBC {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
- }
-
- public static class PKCS7Padding extends CBC {
- public PKCS7Padding() {
- super(KeymasterDefs.KM_PAD_PKCS7);
- }
- }
- }
-
- abstract static class CTR extends AndroidKeyStoreUnauthenticatedAESCipherSpi {
- protected CTR(int keymasterPadding) {
- super(KeymasterDefs.KM_MODE_CTR, keymasterPadding, true);
- }
-
- public static class NoPadding extends CTR {
- public NoPadding() {
- super(KeymasterDefs.KM_PAD_NONE);
- }
- }
- }
-
- private static final int BLOCK_SIZE_BYTES = 16;
-
- private final int mKeymasterBlockMode;
- private final int mKeymasterPadding;
- /** Whether this transformation requires an IV. */
- private final boolean mIvRequired;
-
- private byte[] mIv;
-
- /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
- private boolean mIvHasBeenUsed;
-
- AndroidKeyStoreUnauthenticatedAESCipherSpi(
- int keymasterBlockMode,
- int keymasterPadding,
- boolean ivRequired) {
- mKeymasterBlockMode = keymasterBlockMode;
- mKeymasterPadding = keymasterPadding;
- mIvRequired = ivRequired;
- }
-
- @Override
- protected final void resetAll() {
- mIv = null;
- mIvHasBeenUsed = false;
- super.resetAll();
- }
-
- @Override
- protected final void resetWhilePreservingInitState() {
- super.resetWhilePreservingInitState();
- }
-
- @Override
- protected final void initKey(int opmode, Key key) throws InvalidKeyException {
- if (!(key instanceof AndroidKeyStoreSecretKey)) {
- throw new InvalidKeyException(
- "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
- }
- if (!KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(key.getAlgorithm())) {
- throw new InvalidKeyException(
- "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
- KeyProperties.KEY_ALGORITHM_AES + " supported");
- }
- setKey((AndroidKeyStoreSecretKey) key);
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {
- if (!mIvRequired) {
- return;
- }
-
- // IV is used
- if (!isEncrypting()) {
- throw new InvalidKeyException("IV required when decrypting"
- + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
- }
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
- throws InvalidAlgorithmParameterException {
- if (!mIvRequired) {
- if (params != null) {
- throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
- }
- return;
- }
-
- // IV is used
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException(
- "IvParameterSpec must be provided when decrypting");
- }
- return;
- }
- if (!(params instanceof IvParameterSpec)) {
- throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
- }
- mIv = ((IvParameterSpec) params).getIV();
- if (mIv == null) {
- throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
- }
- }
-
- @Override
- protected final void initAlgorithmSpecificParameters(AlgorithmParameters params)
- throws InvalidAlgorithmParameterException {
- if (!mIvRequired) {
- if (params != null) {
- throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
- }
- return;
- }
-
- // IV is used
- if (params == null) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV required when decrypting"
- + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
- }
- return;
- }
-
- if (!"AES".equalsIgnoreCase(params.getAlgorithm())) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
- + ". Supported: AES");
- }
-
- IvParameterSpec ivSpec;
- try {
- ivSpec = params.getParameterSpec(IvParameterSpec.class);
- } catch (InvalidParameterSpecException e) {
- if (!isEncrypting()) {
- // IV must be provided by the caller
- throw new InvalidAlgorithmParameterException("IV required when decrypting"
- + ", but not found in parameters: " + params, e);
- }
- mIv = null;
- return;
- }
- mIv = ivSpec.getIV();
- if (mIv == null) {
- throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
- }
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForBegin() {
- if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
- // IV will need to be generated
- return BLOCK_SIZE_BYTES;
- }
-
- return 0;
- }
-
- @Override
- protected final int getAdditionalEntropyAmountForFinish() {
- return 0;
- }
-
- @Override
- protected final void addAlgorithmSpecificParametersToBegin(
- @NonNull KeymasterArguments keymasterArgs) {
- if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
- // IV is being reused for encryption: this violates security best practices.
- throw new IllegalStateException(
- "IV has already been used. Reusing IV in encryption mode violates security best"
- + " practices.");
- }
-
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
- keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
- if ((mIvRequired) && (mIv != null)) {
- keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
- }
- }
-
- @Override
- protected final void loadAlgorithmSpecificParametersFromBeginResult(
- @NonNull KeymasterArguments keymasterArgs) {
- mIvHasBeenUsed = true;
-
- // NOTE: Keymaster doesn't always return an IV, even if it's used.
- byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
- if ((returnedIv != null) && (returnedIv.length == 0)) {
- returnedIv = null;
- }
-
- if (mIvRequired) {
- if (mIv == null) {
- mIv = returnedIv;
- } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new ProviderException("IV in use differs from provided IV");
- }
- } else {
- if (returnedIv != null) {
- throw new ProviderException(
- "IV in use despite IV not being used by this transformation");
- }
- }
- }
-
- @Override
- protected final int engineGetBlockSize() {
- return BLOCK_SIZE_BYTES;
- }
-
- @Override
- protected final int engineGetOutputSize(int inputLen) {
- return inputLen + 3 * BLOCK_SIZE_BYTES;
- }
-
- @Override
- protected final byte[] engineGetIV() {
- return ArrayUtils.cloneIfNotEmpty(mIv);
- }
-
- @Nullable
- @Override
- protected final AlgorithmParameters engineGetParameters() {
- if (!mIvRequired) {
- return null;
- }
- if ((mIv != null) && (mIv.length > 0)) {
- try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
- params.init(new IvParameterSpec(mIv));
- return params;
- } catch (NoSuchAlgorithmException e) {
- throw new ProviderException(
- "Failed to obtain AES AlgorithmParameters", e);
- } catch (InvalidParameterSpecException e) {
- throw new ProviderException(
- "Failed to initialize AES AlgorithmParameters with an IV",
- e);
- }
- }
- return null;
- }
-}
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index 1eb8541..be865a0 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -36,9 +36,11 @@
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.SecureRandom;
+import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Random;
import java.util.Set;
@@ -281,8 +283,9 @@
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
+ Certificate[] certs = keyStore.getCertificateChain(keystoreAlias);
X509Certificate[] certificateChain =
- (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias);
+ Arrays.copyOf(certs, certs.length, X509Certificate[].class);
keyStore.deleteEntry(keystoreAlias);
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 9ca551b..1f9022b 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -446,13 +446,6 @@
@UnsupportedAppUsage
@Deprecated
public int getUid() {
- if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
- // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
- // a UID, because we are still being used with the old Keystore SPI.
- // TODO This if statement and body can be removed when the Keystore 2 migration is
- // complete. b/171563717
- return mNamespace;
- }
try {
return KeyProperties.namespaceToLegacyUid(mNamespace);
} catch (IllegalArgumentException e) {
@@ -1021,14 +1014,6 @@
@NonNull
@Deprecated
public Builder setUid(int uid) {
- if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
- // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
- // a UID, because we are still being used with the old Keystore SPI.
- // TODO This if statement and body can be removed when the Keystore 2 migration is
- // complete. b/171563717
- mNamespace = uid;
- return this;
- }
mNamespace = KeyProperties.legacyUidToNamespace(uid);
return this;
}
@@ -1666,9 +1651,10 @@
* Set whether this key is critical to the device encryption flow
*
* This is a special flag only available to system servers to indicate the current key
- * is part of the device encryption flow.
+ * is part of the device encryption flow. Setting this flag causes the key to not
+ * be cryptographically bound to the LSKF even if the key is otherwise authentication
+ * bound.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
*/
public Builder setCriticalToDeviceEncryption(boolean critical) {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 673491e..88d1a5b 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -24,6 +24,7 @@
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.security.GateKeeper;
+import android.security.keystore2.KeymasterUtils;
import java.security.Key;
import java.security.KeyStore.ProtectionParameter;
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
deleted file mode 100644
index 2c0f40d..0000000
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.os.IBinder;
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-import android.security.keymaster.KeymasterDefs;
-import android.security.keymaster.OperationResult;
-
-import libcore.util.EmptyArray;
-
-/**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
- *
- * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's
- * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
- * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
- * operation may consume less data than provided, in which case the caller has to buffer the
- * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update
- * and passing input data directly to final improves performance. This threshold is configurable;
- * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of
- * operations (e.g. ciphers).
- *
- * <p>The helper exposes {@link #update(byte[], int, int) update} and
- * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
- * conveniently implement various JCA crypto primitives.
- *
- * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
- * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
- * parameters to {@code update} and {@code final} operations.
- *
- * @hide
- */
-class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
-
- /**
- * Bidirectional chunked data stream over a KeyStore crypto operation.
- */
- interface Stream {
- /**
- * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
- * be reached.
- */
- OperationResult update(byte[] input);
-
- /**
- * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
- * be reached.
- */
- OperationResult finish(byte[] input, byte[] siganture, byte[] additionalEntropy);
- }
-
- // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
- // Thus, it's safer to use a much smaller upper bound.
- private static final int DEFAULT_CHUNK_SIZE_MAX = 64 * 1024;
- // The chunk buffer will be sent to update until its size under this threshold.
- // This threshold should be <= the max input allowed for finish.
- // Setting this threshold <= 1 will effectivley disable buffering between updates.
- private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024;
-
- private final Stream mKeyStoreStream;
- private final int mChunkSizeMax;
- private final int mChunkSizeThreshold;
- private final byte[] mChunk;
- private int mChunkLength = 0;
- private long mConsumedInputSizeBytes;
- private long mProducedOutputSizeBytes;
-
- KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
- this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX);
- }
-
- KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) {
- this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX);
- }
-
- KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold,
- int chunkSizeMax) {
- mKeyStoreStream = operation;
- mChunkSizeMax = chunkSizeMax;
- if (chunkSizeThreshold <= 0) {
- mChunkSizeThreshold = 1;
- } else if (chunkSizeThreshold > chunkSizeMax) {
- mChunkSizeThreshold = chunkSizeMax;
- } else {
- mChunkSizeThreshold = chunkSizeThreshold;
- }
- mChunk = new byte[mChunkSizeMax];
- }
-
- public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
- if (inputLength == 0 || input == null) {
- // No input provided
- return EmptyArray.BYTE;
- }
- if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
- "Input offset and length out of bounds of input array");
- }
-
- byte[] output = EmptyArray.BYTE;
-
- while (inputLength > 0 || mChunkLength >= mChunkSizeThreshold) {
- int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength,
- inputLength);
- inputLength -= inputConsumed;
- inputOffset += inputConsumed;
- mChunkLength += inputConsumed;
- mConsumedInputSizeBytes += inputConsumed;
-
- if (mChunkLength > mChunkSizeMax) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
- "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax
- + " Actual: " + mChunkLength);
- }
-
- if (mChunkLength >= mChunkSizeThreshold) {
- OperationResult opResult = mKeyStoreStream.update(
- ArrayUtils.subarray(mChunk, 0, mChunkLength));
-
- if (opResult == null) {
- throw new KeyStoreConnectException();
- } else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getKeyStoreException(opResult.resultCode);
- }
- if (opResult.inputConsumed <= 0) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
- "Keystore consumed 0 of " + mChunkLength + " bytes provided.");
- } else if (opResult.inputConsumed > mChunkLength) {
- throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
- "Keystore consumed more input than provided. Provided: "
- + mChunkLength + ", consumed: " + opResult.inputConsumed);
- }
- mChunkLength -= opResult.inputConsumed;
-
- if (mChunkLength > 0) {
- // Partialy consumed, shift chunk contents
- ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength);
- }
-
- if ((opResult.output != null) && (opResult.output.length > 0)) {
- // Output was produced
- mProducedOutputSizeBytes += opResult.output.length;
- output = ArrayUtils.concat(output, opResult.output);
- }
- }
- }
- return output;
- }
-
- public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
- byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
- byte[] output = update(input, inputOffset, inputLength);
- byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength);
- OperationResult opResult = mKeyStoreStream.finish(finalChunk, signature, additionalEntropy);
-
- if (opResult == null) {
- throw new KeyStoreConnectException();
- } else if (opResult.resultCode != KeyStore.NO_ERROR) {
- throw KeyStore.getKeyStoreException(opResult.resultCode);
- }
- // If no error, assume all input consumed
- mConsumedInputSizeBytes += finalChunk.length;
-
- if ((opResult.output != null) && (opResult.output.length > 0)) {
- mProducedOutputSizeBytes += opResult.output.length;
- output = ArrayUtils.concat(output, opResult.output);
- }
-
- return output;
- }
-
- @Override
- public long getConsumedInputSizeBytes() {
- return mConsumedInputSizeBytes;
- }
-
- @Override
- public long getProducedOutputSizeBytes() {
- return mProducedOutputSizeBytes;
- }
-
- /**
- * Main data stream via a KeyStore streaming operation.
- *
- * <p>For example, for an encryption operation, this is the stream through which plaintext is
- * provided and ciphertext is obtained.
- */
- public static class MainDataStream implements Stream {
-
- private final KeyStore mKeyStore;
- private final IBinder mOperationToken;
-
- public MainDataStream(KeyStore keyStore, IBinder operationToken) {
- mKeyStore = keyStore;
- mOperationToken = operationToken;
- }
-
- @Override
- public OperationResult update(byte[] input) {
- return mKeyStore.update(mOperationToken, null, input);
- }
-
- @Override
- public OperationResult finish(byte[] input, byte[] signature, byte[] additionalEntropy) {
- return mKeyStore.finish(mOperationToken, null, input, signature, additionalEntropy);
- }
- }
-}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
deleted file mode 100644
index 062c2d4..0000000
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.KeyStore;
-import android.security.KeyStoreException;
-
-/**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
- *
- * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
- * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
- * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
- * operation may consume less data than provided, in which case the caller has to buffer the
- * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
- * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
- * conveniently implement various JCA crypto primitives.
- *
- * @hide
- */
-interface KeyStoreCryptoOperationStreamer {
- byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
- byte[] doFinal(byte[] input, int inputOffset, int inputLength, byte[] signature,
- byte[] additionalEntropy) throws KeyStoreException;
- long getConsumedInputSizeBytes();
- long getProducedOutputSizeBytes();
-}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
deleted file mode 100644
index c82b6e6b..0000000
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationUtils.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.security.KeyStore;
-import android.security.keymaster.KeymasterDefs;
-
-import libcore.util.EmptyArray;
-
-import java.security.GeneralSecurityException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.SecureRandom;
-
-/**
- * Assorted utility methods for implementing crypto operations on top of KeyStore.
- *
- * @hide
- */
-abstract class KeyStoreCryptoOperationUtils {
-
- private static volatile SecureRandom sRng;
-
- private KeyStoreCryptoOperationUtils() {}
-
- /**
- * Returns the {@link InvalidKeyException} to be thrown by the {@code init} method of
- * the crypto operation in response to {@code KeyStore.begin} operation or {@code null} if
- * the {@code init} method should succeed.
- */
- static InvalidKeyException getInvalidKeyExceptionForInit(
- KeyStore keyStore, AndroidKeyStoreKey key, int beginOpResultCode) {
- if (beginOpResultCode == KeyStore.NO_ERROR) {
- return null;
- }
-
- // An error occurred. However, some errors should not lead to init throwing an exception.
- // See below.
- InvalidKeyException e =
- keyStore.getInvalidKeyException(key.getAlias(), key.getUid(), beginOpResultCode);
- switch (beginOpResultCode) {
- case KeyStore.OP_AUTH_NEEDED:
- // Operation needs to be authorized by authenticating the user. Don't throw an
- // exception is such authentication is possible for this key
- // (UserNotAuthenticatedException). An example of when it's not possible is where
- // the key is permanently invalidated (KeyPermanentlyInvalidatedException).
- if (e instanceof UserNotAuthenticatedException) {
- return null;
- }
- break;
- }
- return e;
- }
-
- /**
- * Returns the exception to be thrown by the {@code Cipher.init} method of the crypto operation
- * in response to {@code KeyStore.begin} operation or {@code null} if the {@code init} method
- * should succeed.
- */
- public static GeneralSecurityException getExceptionForCipherInit(
- KeyStore keyStore, AndroidKeyStoreKey key, int beginOpResultCode) {
- if (beginOpResultCode == KeyStore.NO_ERROR) {
- return null;
- }
-
- // Cipher-specific cases
- switch (beginOpResultCode) {
- case KeymasterDefs.KM_ERROR_INVALID_NONCE:
- return new InvalidAlgorithmParameterException("Invalid IV");
- case KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED:
- return new InvalidAlgorithmParameterException("Caller-provided IV not permitted");
- }
-
- // General cases
- return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode);
- }
-
- /**
- * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
- *
- * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
- * RNG.
- */
- static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
- if (sizeBytes <= 0) {
- return EmptyArray.BYTE;
- }
- if (rng == null) {
- rng = getRng();
- }
- byte[] result = new byte[sizeBytes];
- rng.nextBytes(result);
- return result;
- }
-
- private static SecureRandom getRng() {
- // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
- // required to be thread-safe.
- if (sRng == null) {
- sRng = new SecureRandom();
- }
- return sRng;
- }
-}
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
deleted file mode 100644
index 670ef5e..0000000
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.hardware.biometrics.BiometricManager;
-import android.security.GateKeeper;
-import android.security.KeyStore;
-import android.security.keymaster.KeymasterArguments;
-import android.security.keymaster.KeymasterDefs;
-
-import java.security.ProviderException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public abstract class KeymasterUtils {
-
- private KeymasterUtils() {}
-
- public static int getDigestOutputSizeBits(int keymasterDigest) {
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_NONE:
- return -1;
- case KeymasterDefs.KM_DIGEST_MD5:
- return 128;
- case KeymasterDefs.KM_DIGEST_SHA1:
- return 160;
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- return 224;
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- return 256;
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- return 384;
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- return 512;
- default:
- throw new IllegalArgumentException("Unknown digest: " + keymasterDigest);
- }
- }
-
- public static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
- int keymasterBlockMode) {
- switch (keymasterBlockMode) {
- case KeymasterDefs.KM_MODE_ECB:
- return false;
- case KeymasterDefs.KM_MODE_CBC:
- case KeymasterDefs.KM_MODE_CTR:
- case KeymasterDefs.KM_MODE_GCM:
- return true;
- default:
- throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
- }
- }
-
- public static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
- int keymasterPadding) {
- switch (keymasterPadding) {
- case KeymasterDefs.KM_PAD_NONE:
- return false;
- case KeymasterDefs.KM_PAD_RSA_OAEP:
- case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
- return true;
- default:
- throw new IllegalArgumentException(
- "Unsupported asymmetric encryption padding scheme: " + keymasterPadding);
- }
- }
-
- private static void addSids(KeymasterArguments args, UserAuthArgs spec) {
- // If both biometric and credential are accepted, then just use the root sid from gatekeeper
- if (spec.getUserAuthenticationType() == (KeyProperties.AUTH_BIOMETRIC_STRONG
- | KeyProperties.AUTH_DEVICE_CREDENTIAL)) {
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(spec.getBoundToSpecificSecureUserId()));
- } else {
- // The key is authorized for use for the specified amount of time after the user has
- // authenticated. Whatever unlocks the secure lock screen should authorize this key.
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(getRootSid()));
- }
- } else {
- List<Long> sids = new ArrayList<>();
- if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_BIOMETRIC_STRONG) != 0) {
- final BiometricManager bm = KeyStore.getApplicationContext()
- .getSystemService(BiometricManager.class);
-
- // TODO: Restore permission check in getAuthenticatorIds once the ID is no longer
- // needed here.
-
- final long[] biometricSids = bm.getAuthenticatorIds();
-
- if (biometricSids.length == 0) {
- throw new IllegalStateException(
- "At least one biometric must be enrolled to create keys requiring user"
- + " authentication for every use");
- }
-
- if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
- sids.add(spec.getBoundToSpecificSecureUserId());
- } else if (spec.isInvalidatedByBiometricEnrollment()) {
- // The biometric-only SIDs will change on biometric enrollment or removal of all
- // enrolled templates, invalidating the key.
- for (long sid : biometricSids) {
- sids.add(sid);
- }
- } else {
- // The root SID will *not* change on fingerprint enrollment, or removal of all
- // enrolled fingerprints, allowing the key to remain valid.
- sids.add(getRootSid());
- }
- } else if ((spec.getUserAuthenticationType() & KeyProperties.AUTH_DEVICE_CREDENTIAL)
- != 0) {
- sids.add(getRootSid());
- } else {
- throw new IllegalStateException("Invalid or no authentication type specified.");
- }
-
- for (int i = 0; i < sids.size(); i++) {
- args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
- KeymasterArguments.toUint64(sids.get(i)));
- }
- }
- }
-
- /**
- * Adds keymaster arguments to express the key's authorization policy supported by user
- * authentication.
- *
- * @param args The arguments sent to keymaster that need to be populated from the spec
- * @param spec The user authentication relevant portions of the spec passed in from the caller.
- * This spec will be translated into the relevant keymaster tags to be loaded into args.
- * @throws IllegalStateException if user authentication is required but the system is in a wrong
- * state (e.g., secure lock screen not set up) for generating or importing keys that
- * require user authentication.
- */
- public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) {
-
- if (spec.isUserConfirmationRequired()) {
- args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
- }
-
- if (spec.isUserPresenceRequired()) {
- args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
- }
-
- if (spec.isUnlockedDeviceRequired()) {
- args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
- }
-
- if (!spec.isUserAuthenticationRequired()) {
- args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
- return;
- }
-
- if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
- // Every use of this key needs to be authorized by the user.
- addSids(args, spec);
- args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
-
- if (spec.isUserAuthenticationValidWhileOnBody()) {
- throw new ProviderException("Key validity extension while device is on-body is not "
- + "supported for keys requiring fingerprint authentication");
- }
- } else {
- addSids(args, spec);
- args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
- args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
- spec.getUserAuthenticationValidityDurationSeconds());
- if (spec.isUserAuthenticationValidWhileOnBody()) {
- args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
- }
- }
- }
-
- /**
- * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for
- * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC,
- * AES-GCM).
- */
- public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args,
- int keymasterAlgorithm,
- int[] keymasterBlockModes,
- int[] keymasterDigests) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_AES:
- if (com.android.internal.util.ArrayUtils.contains(
- keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) {
- // AES GCM key needs the minimum length of AEAD tag specified.
- args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
- AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
- .MIN_SUPPORTED_TAG_LENGTH_BITS);
- }
- break;
- case KeymasterDefs.KM_ALGORITHM_HMAC:
- // HMAC key needs the minimum length of MAC set to the output size of the associated
- // digest. This is because we do not offer a way to generate shorter MACs and
- // don't offer a way to verify MACs (other than by generating them).
- if (keymasterDigests.length != 1) {
- throw new ProviderException(
- "Unsupported number of authorized digests for HMAC key: "
- + keymasterDigests.length
- + ". Exactly one digest must be authorized");
- }
- int keymasterDigest = keymasterDigests[0];
- int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest);
- if (digestOutputSizeBits == -1) {
- throw new ProviderException(
- "HMAC key authorized for unsupported digest: "
- + KeyProperties.Digest.fromKeymaster(keymasterDigest));
- }
- args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits);
- break;
- }
- }
-
- private static long getRootSid() {
- long rootSid = GateKeeper.getSecureUserId();
- if (rootSid == 0) {
- throw new IllegalStateException("Secure lock screen must be enabled"
- + " to create keys requiring user authentication");
- }
- return rootSid;
- }
-}
diff --git a/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java
index d1cc572..c1842b4 100644
--- a/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java
+++ b/keystore/java/android/security/keystore/SecureKeyImportUnavailableException.java
@@ -16,8 +16,8 @@
package android.security.keystore;
-import android.security.KeyStore;
import android.security.KeyStoreException;
+import android.security.keymaster.KeymasterDefs;
import java.security.ProviderException;
@@ -31,7 +31,7 @@
}
public SecureKeyImportUnavailableException(String message) {
- super(message, new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE,
+ super(message, new KeyStoreException(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
"Secure Key Import not available"));
}
diff --git a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java
index 6c7e9a9..1f4e12e 100644
--- a/keystore/java/android/security/keystore/StrongBoxUnavailableException.java
+++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java
@@ -16,8 +16,8 @@
package android.security.keystore;
-import android.security.KeyStore;
import android.security.KeyStoreException;
+import android.security.keymaster.KeymasterDefs;
import java.security.ProviderException;
@@ -33,7 +33,8 @@
public StrongBoxUnavailableException(String message) {
super(message,
- new KeyStoreException(KeyStore.HARDWARE_TYPE_UNAVAILABLE, "No StrongBox available")
+ new KeyStoreException(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
+ "No StrongBox available")
);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
index 0f77749..268b15bf 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreHmacSpi.java
@@ -21,7 +21,6 @@
import android.security.KeyStoreOperation;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyStoreCryptoOperation;
-import android.security.keystore.KeymasterUtils;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 1575bb4..f1681ec 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -20,12 +20,10 @@
import android.hardware.security.keymint.SecurityLevel;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
-import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.ArrayUtils;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.security.keystore.KeymasterUtils;
import android.security.keystore.StrongBoxUnavailableException;
import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreSecurityLevel;
@@ -259,7 +257,7 @@
// Check that user authentication related parameters are acceptable. This method
// will throw an IllegalStateException if there are issues (e.g., secure lock screen
// not set up).
- KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
+ KeyStore2ParameterUtils.addUserAuthArgs(new ArrayList<>(), spec);
} catch (IllegalStateException | IllegalArgumentException e) {
throw new InvalidAlgorithmParameterException(e);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 2d8901a..c26d9f583 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.Context;
import android.hardware.security.keymint.KeyParameter;
import android.hardware.security.keymint.KeyPurpose;
@@ -28,7 +29,6 @@
import android.security.GenerateRkpKey;
import android.security.GenerateRkpKeyException;
import android.security.KeyPairGeneratorSpec;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStoreException;
import android.security.KeyStoreSecurityLevel;
@@ -39,7 +39,6 @@
import android.security.keystore.DeviceIdAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
-import android.security.keystore.KeymasterUtils;
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.StrongBoxUnavailableException;
import android.system.keystore2.Authorization;
@@ -270,7 +269,7 @@
// Check that user authentication related parameters are acceptable. This method
// will throw an IllegalStateException if there are issues (e.g., secure lock screen
// not set up).
- KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
+ KeyStore2ParameterUtils.addUserAuthArgs(new ArrayList<>(), mSpec);
} catch (IllegalArgumentException | IllegalStateException e) {
throw new InvalidAlgorithmParameterException(e);
}
@@ -572,7 +571,8 @@
AndroidKeyStorePublicKey publicKey =
AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
- GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+ GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
+ .currentApplication());
try {
if (mSpec.getAttestationChallenge() != null) {
keyGen.notifyKeyGenerated(securityLevel);
@@ -589,7 +589,8 @@
case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
case ResponseCode.OUT_OF_KEYS:
- GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
+ GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
+ .currentApplication());
try {
keyGen.notifyEmpty(securityLevel);
} catch (RemoteException f) {
@@ -665,8 +666,8 @@
if (idTypesSet.contains(AttestationUtils.ID_TYPE_IMEI)
|| idTypesSet.contains(AttestationUtils.ID_TYPE_MEID)) {
telephonyService =
- (TelephonyManager) KeyStore.getApplicationContext().getSystemService(
- Context.TELEPHONY_SERVICE);
+ (TelephonyManager) android.app.AppGlobals.getInitialApplication()
+ .getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyService == null) {
throw new DeviceIdAttestationException("Unable to access telephony service");
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index ba6d22f..89d2b74 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -110,23 +110,6 @@
putSecretKeyFactoryImpl("HmacSHA512");
}
- private static boolean sInstalled = false;
-
- /**
- * This function indicates whether or not this provider was installed. This is manly used
- * as indicator for
- * {@link android.security.keystore.AndroidKeyStoreProvider#getKeyStoreForUid(int)}
- * to whether or not to retrieve the Keystore provider by "AndroidKeyStoreLegacy".
- * This function can be removed once the transition to Keystore 2.0 is complete.
- * b/171305684
- *
- * @return true if this provider was installed.
- * @hide
- */
- public static boolean isInstalled() {
- return sInstalled;
- }
-
/**
* Installs a new instance of this provider (and the
* {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -142,7 +125,6 @@
break;
}
}
- sInstalled = true;
Security.addProvider(new AndroidKeyStoreProvider());
Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 6ff9432..5848247 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -21,7 +21,6 @@
import android.hardware.security.keymint.KeyParameter;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
-import android.security.keystore.KeymasterUtils;
import android.system.keystore2.Authorization;
import java.security.AlgorithmParameters;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 32f98a2..3e2fb94 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -30,7 +30,6 @@
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
-import android.security.keystore.KeymasterUtils;
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.WrappedKeyEntry;
import android.system.keystore2.AuthenticatorSpec;
diff --git a/keystore/java/android/security/keystore2/KeymasterUtils.java b/keystore/java/android/security/keystore2/KeymasterUtils.java
new file mode 100644
index 0000000..de4696c
--- /dev/null
+++ b/keystore/java/android/security/keystore2/KeymasterUtils.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.KeyProperties;
+
+import java.security.ProviderException;
+
+/**
+ * @hide
+ */
+public abstract class KeymasterUtils {
+
+ private KeymasterUtils() {}
+
+ /** @hide */
+ static int getDigestOutputSizeBits(int keymasterDigest) {
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return -1;
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return 128;
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return 160;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return 224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return 256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return 384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return 512;
+ default:
+ throw new IllegalArgumentException("Unknown digest: " + keymasterDigest);
+ }
+ }
+
+ /** @hide */
+ static boolean isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
+ int keymasterBlockMode) {
+ switch (keymasterBlockMode) {
+ case KeymasterDefs.KM_MODE_ECB:
+ return false;
+ case KeymasterDefs.KM_MODE_CBC:
+ case KeymasterDefs.KM_MODE_CTR:
+ case KeymasterDefs.KM_MODE_GCM:
+ return true;
+ default:
+ throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
+ }
+ }
+
+ /** @hide */
+ static boolean isKeymasterPaddingSchemeIndCpaCompatibleWithAsymmetricCrypto(
+ int keymasterPadding) {
+ switch (keymasterPadding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return false;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return true;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported asymmetric encryption padding scheme: " + keymasterPadding);
+ }
+ }
+
+ /**
+ * Adds {@code KM_TAG_MIN_MAC_LENGTH} tag, if necessary, to the keymaster arguments for
+ * generating or importing a key. This tag may only be needed for symmetric keys (e.g., HMAC,
+ * AES-GCM).
+ */
+ public static void addMinMacLengthAuthorizationIfNecessary(KeymasterArguments args,
+ int keymasterAlgorithm,
+ int[] keymasterBlockModes,
+ int[] keymasterDigests) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_AES:
+ if (com.android.internal.util.ArrayUtils.contains(
+ keymasterBlockModes, KeymasterDefs.KM_MODE_GCM)) {
+ // AES GCM key needs the minimum length of AEAD tag specified.
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH,
+ AndroidKeyStoreAuthenticatedAESCipherSpi.GCM
+ .MIN_SUPPORTED_TAG_LENGTH_BITS);
+ }
+ break;
+ case KeymasterDefs.KM_ALGORITHM_HMAC:
+ // HMAC key needs the minimum length of MAC set to the output size of the associated
+ // digest. This is because we do not offer a way to generate shorter MACs and
+ // don't offer a way to verify MACs (other than by generating them).
+ if (keymasterDigests.length != 1) {
+ throw new ProviderException(
+ "Unsupported number of authorized digests for HMAC key: "
+ + keymasterDigests.length
+ + ". Exactly one digest must be authorized");
+ }
+ int keymasterDigest = keymasterDigests[0];
+ int digestOutputSizeBits = getDigestOutputSizeBits(keymasterDigest);
+ if (digestOutputSizeBits == -1) {
+ throw new ProviderException(
+ "HMAC key authorized for unsupported digest: "
+ + KeyProperties.Digest.fromKeymaster(keymasterDigest));
+ }
+ args.addUnsignedInt(KeymasterDefs.KM_TAG_MIN_MAC_LENGTH, digestOutputSizeBits);
+ break;
+ }
+ }
+}
diff --git a/native/android/OWNERS b/native/android/OWNERS
index d414ed4..6f7def8 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -2,3 +2,7 @@
per-file libandroid_net.map.txt, net.c = codewiz@google.com, jchalard@google.com, junyulai@google.com
per-file libandroid_net.map.txt, net.c = lorenzo@google.com, reminv@google.com, satk@google.com
per-file system_fonts.cpp = file:/graphics/java/android/graphics/fonts/OWNERS
+
+per-file native_window_jni.cpp = file:/services/core/java/com/android/server/wm/OWNERS
+per-file native_activity.cpp = file:/services/core/java/com/android/server/wm/OWNERS
+per-file surface_control.cpp = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 550e324..10d68ba 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -50,7 +50,7 @@
import android.widget.TextView;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.TrafficStatsConstants;
+import com.android.net.module.util.NetworkStackConstants;
import java.io.IOException;
import java.lang.reflect.Field;
@@ -238,7 +238,7 @@
HttpURLConnection urlConnection = null;
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ NetworkStackConstants.TAG_SYSTEM_PROBE);
try {
urlConnection = (HttpURLConnection) mNetwork.openConnection(
new URL(mCm.getCaptivePortalServerUrl()));
diff --git a/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
index 0d70f2a..3495efc 100644
--- a/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
@@ -18,5 +18,5 @@
package android.net;
// @JavaOnlyStableParcelable only affects the parcelable when built as stable aidl (aidl_interface
-// build rule). IpPrefix is also used in cpp but only as non-stable aidl.
-@JavaOnlyStableParcelable parcelable IpPrefix cpp_header "binder/IpPrefix.h";
+// build rule).
+@JavaOnlyStableParcelable parcelable IpPrefix;
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index 0a9560a..ab290f9 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -298,7 +298,6 @@
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
method public int getOwnerUid();
method public int getSignalStrength();
- method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
@@ -408,7 +407,6 @@
method public android.net.NetworkRequest.Builder removeTransportType(int);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
- method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
}
public class ParseException extends java.lang.RuntimeException {
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index cd96a1b..f2f5c1b 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -176,10 +176,12 @@
}
public final class VpnTransportInfo implements android.os.Parcelable android.net.TransportInfo {
- ctor public VpnTransportInfo(int);
+ ctor public VpnTransportInfo(int, @Nullable String);
method public int describeContents();
+ method @NonNull public android.net.VpnTransportInfo makeCopy(long);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.VpnTransportInfo> CREATOR;
+ field @Nullable public final String sessionId;
field public final int type;
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index afd4fa0..0a82cb7 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -273,7 +273,9 @@
public final class NetworkCapabilities implements android.os.Parcelable {
method @NonNull public int[] getAdministratorUids();
+ method @Nullable public static String getCapabilityCarrierName(int);
method @Nullable public String getSsid();
+ method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
method @NonNull public int[] getTransportTypes();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
@@ -327,6 +329,7 @@
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
+ method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
}
public final class NetworkScore implements android.os.Parcelable {
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index c6f4e0b..92ed7fc 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3140,18 +3140,27 @@
}
/**
- * Set a network-independent global http proxy. This is not normally what you want
- * for typical HTTP proxies - they are general network dependent. However if you're
- * doing something unusual like general internal filtering this may be useful. On
- * a private network where the proxy is not accessible, you may break HTTP using this.
+ * Set a network-independent global HTTP proxy.
*
- * @param p A {@link ProxyInfo} object defining the new global
- * HTTP proxy. A {@code null} value will clear the global HTTP proxy.
+ * This sets an HTTP proxy that applies to all networks and overrides any network-specific
+ * proxy. If set, HTTP libraries that are proxy-aware will use this global proxy when
+ * accessing any network, regardless of what the settings for that network are.
+ *
+ * Note that HTTP proxies are by nature typically network-dependent, and setting a global
+ * proxy is likely to break networking on multiple networks. This method is only meant
+ * for device policy clients looking to do general internal filtering or similar use cases.
+ *
+ * {@see #getGlobalProxy}
+ * {@see LinkProperties#getHttpProxy}
+ *
+ * @param p A {@link ProxyInfo} object defining the new global HTTP proxy. Calling this
+ * method with a {@code null} value will clear the global HTTP proxy.
* @hide
*/
+ // Used by Device Policy Manager to set the global proxy.
@SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
- public void setGlobalProxy(@Nullable ProxyInfo p) {
+ public void setGlobalProxy(@Nullable final ProxyInfo p) {
try {
mService.setGlobalProxy(p);
} catch (RemoteException e) {
diff --git a/packages/Connectivity/framework/src/android/net/DnsResolver.java b/packages/Connectivity/framework/src/android/net/DnsResolver.java
index 3f7660f..dac88ad 100644
--- a/packages/Connectivity/framework/src/android/net/DnsResolver.java
+++ b/packages/Connectivity/framework/src/android/net/DnsResolver.java
@@ -500,7 +500,7 @@
try {
resp = resNetworkResult(fd); // Closes fd, marks it invalid.
} catch (ErrnoException e) {
- Log.e(TAG, "resNetworkResult:" + e.toString());
+ Log.w(TAG, "resNetworkResult:" + e.toString());
exception = e;
}
}
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 7e70891..d39f4fb 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -728,6 +728,23 @@
return ((mNetworkCapabilities & CONNECTIVITY_MANAGED_CAPABILITIES) != 0);
}
+ /**
+ * Get the name of the given capability that carriers use.
+ * If the capability does not have a carrier-name, returns null.
+ *
+ * @param capability The capability to get the carrier-name of.
+ * @return The carrier-name of the capability, or null if it doesn't exist.
+ * @hide
+ */
+ @SystemApi
+ public static @Nullable String getCapabilityCarrierName(@NetCapability int capability) {
+ if (capability == NET_CAPABILITY_ENTERPRISE) {
+ return capabilityNameOf(capability);
+ } else {
+ return null;
+ }
+ }
+
private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
final long unwantedCaps =
@@ -2346,9 +2363,15 @@
/**
* Gets the subscription ID set that associated to this network or request.
+ *
+ * <p>Instances of NetworkCapabilities will only have this field populated by the system if the
+ * receiver holds the NETWORK_FACTORY permission. In all other cases, it will be the empty set.
+ *
* @return
+ * @hide
*/
@NonNull
+ @SystemApi
public Set<Integer> getSubIds() {
return new ArraySet<>(mSubIds);
}
@@ -2713,10 +2736,17 @@
/**
* Set the subscription ID set.
*
+ * <p>SubIds are populated in NetworkCapability instances from the system only for callers
+ * that hold the NETWORK_FACTORY permission. Similarly, the system will reject any
+ * NetworkRequests filed with a non-empty set of subIds unless the caller holds the
+ * NETWORK_FACTORY permission.
+ *
* @param subIds a set that represent the subscription IDs. Empty if clean up.
* @return this builder.
+ * @hide
*/
@NonNull
+ @SystemApi
public Builder setSubIds(@NonNull final Set<Integer> subIds) {
mCaps.setSubIds(subIds);
return this;
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 38691ef..5d40417 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -501,9 +501,14 @@
* A network will satisfy this request only if it matches one of the subIds in this set.
* An empty set matches all networks, including those without a subId.
*
+ * <p>Registering a NetworkRequest with a non-empty set of subIds requires the
+ * NETWORK_FACTORY permission.
+ *
* @param subIds A {@code Set} that represents subscription IDs.
+ * @hide
*/
@NonNull
+ @SystemApi
public Builder setSubIds(@NonNull Set<Integer> subIds) {
mNetworkCapabilities.setSubIds(subIds);
return this;
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index cd8f4c0..ba83a44 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -17,11 +17,14 @@
package android.net;
import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -38,8 +41,26 @@
/** Type of this VPN. */
public final int type;
- public VpnTransportInfo(int type) {
+ @Nullable
+ public final String sessionId;
+
+ @Override
+ public long getApplicableRedactions() {
+ return REDACT_FOR_NETWORK_SETTINGS;
+ }
+
+ /**
+ * Create a copy of a {@link VpnTransportInfo} with the sessionId redacted if necessary.
+ */
+ @NonNull
+ public VpnTransportInfo makeCopy(long redactions) {
+ return new VpnTransportInfo(type,
+ ((redactions & REDACT_FOR_NETWORK_SETTINGS) != 0) ? null : sessionId);
+ }
+
+ public VpnTransportInfo(int type, @Nullable String sessionId) {
this.type = type;
+ this.sessionId = sessionId;
}
@Override
@@ -47,17 +68,17 @@
if (!(o instanceof VpnTransportInfo)) return false;
VpnTransportInfo that = (VpnTransportInfo) o;
- return this.type == that.type;
+ return (this.type == that.type) && TextUtils.equals(this.sessionId, that.sessionId);
}
@Override
public int hashCode() {
- return Objects.hash(type);
+ return Objects.hash(type, sessionId);
}
@Override
public String toString() {
- return String.format("VpnTransportInfo{type=%d}", type);
+ return String.format("VpnTransportInfo{type=%d, sessionId=%s}", type, sessionId);
}
@Override
@@ -68,12 +89,13 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(type);
+ dest.writeString(sessionId);
}
public static final @NonNull Creator<VpnTransportInfo> CREATOR =
new Creator<VpnTransportInfo>() {
public VpnTransportInfo createFromParcel(Parcel in) {
- return new VpnTransportInfo(in.readInt());
+ return new VpnTransportInfo(in.readInt(), in.readString());
}
public VpnTransportInfo[] newArray(int size) {
return new VpnTransportInfo[size];
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index c57d4ad..cbb5105 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -66,31 +66,40 @@
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
+ WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+ updateWifiInfo(wifiInfo);
+ updateStatusLabel();
+ mCallback.run();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ updateWifiInfo(null);
updateStatusLabel();
mCallback.run();
}
};
private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
- // network is now the default network, and its capabilities are nc.
- // This method will always be called immediately after the network becomes the
- // default, in addition to any time the capabilities change while the network is
- // the default.
- mDefaultNetwork = network;
- mDefaultNetworkCapabilities = nc;
- updateStatusLabel();
- mCallback.run();
- }
- @Override
- public void onLost(Network network) {
- // The system no longer has a default network.
- mDefaultNetwork = null;
- mDefaultNetworkCapabilities = null;
- updateStatusLabel();
- mCallback.run();
- }
- };
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ // network is now the default network, and its capabilities are nc.
+ // This method will always be called immediately after the network becomes the
+ // default, in addition to any time the capabilities change while the network is
+ // the default.
+ mDefaultNetwork = network;
+ mDefaultNetworkCapabilities = nc;
+ updateStatusLabel();
+ mCallback.run();
+ }
+ @Override
+ public void onLost(Network network) {
+ // The system no longer has a default network.
+ mDefaultNetwork = null;
+ mDefaultNetworkCapabilities = null;
+ updateStatusLabel();
+ mCallback.run();
+ }
+ };
private Network mDefaultNetwork = null;
private NetworkCapabilities mDefaultNetworkCapabilities = null;
private final Runnable mCallback;
@@ -170,32 +179,22 @@
String action = intent.getAction();
if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
updateWifiState();
- } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- updateWifiState();
- final NetworkInfo networkInfo =
- intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- connected = networkInfo != null && networkInfo.isConnected();
- mWifiInfo = null;
- ssid = null;
- if (connected) {
- mWifiInfo = mWifiManager.getConnectionInfo();
- if (mWifiInfo != null) {
- if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
- ssid = mWifiInfo.getPasspointProviderFriendlyName();
- } else {
- ssid = getValidSsid(mWifiInfo);
- }
- updateRssi(mWifiInfo.getRssi());
- maybeRequestNetworkScore();
- }
+ }
+ }
+
+ private void updateWifiInfo(WifiInfo wifiInfo) {
+ updateWifiState();
+ connected = wifiInfo != null;
+ mWifiInfo = wifiInfo;
+ ssid = null;
+ if (mWifiInfo != null) {
+ if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
+ ssid = mWifiInfo.getPasspointProviderFriendlyName();
+ } else {
+ ssid = getValidSsid(mWifiInfo);
}
- updateStatusLabel();
- mCallback.run();
- } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
- // Default to -200 as its below WifiManager.MIN_RSSI.
- updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200));
- updateStatusLabel();
- mCallback.run();
+ updateRssi(mWifiInfo.getRssi());
+ maybeRequestNetworkScore();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index df00a4f..7e3ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -344,8 +344,6 @@
// broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 4ae9665..3c0b07a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -101,11 +101,7 @@
wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
}
- /**
- * Fetches wifi initial state replacing the initial sticky broadcast.
- */
- public void fetchInitialState() {
- mWifiTracker.fetchInitialState();
+ private void copyWifiStates() {
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
mCurrentState.connected = mWifiTracker.connected;
@@ -113,6 +109,14 @@
mCurrentState.rssi = mWifiTracker.rssi;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ }
+
+ /**
+ * Fetches wifi initial state replacing the initial sticky broadcast.
+ */
+ public void fetchInitialState() {
+ mWifiTracker.fetchInitialState();
+ copyWifiStates();
notifyListenersIfNecessary();
}
@@ -121,19 +125,12 @@
*/
public void handleBroadcast(Intent intent) {
mWifiTracker.handleBroadcast(intent);
- mCurrentState.enabled = mWifiTracker.enabled;
- mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
- mCurrentState.connected = mWifiTracker.connected;
- mCurrentState.ssid = mWifiTracker.ssid;
- mCurrentState.rssi = mWifiTracker.rssi;
- mCurrentState.level = mWifiTracker.level;
- mCurrentState.statusLabel = mWifiTracker.statusLabel;
+ copyWifiStates();
notifyListenersIfNecessary();
}
private void handleStatusUpdated() {
- mCurrentState.statusLabel = mWifiTracker.statusLabel;
- mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
+ copyWifiStates();
notifyListenersIfNecessary();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 3a84e31..0dfcf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -40,7 +40,9 @@
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.NetworkScoreManager;
+import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.provider.Settings;
@@ -51,7 +53,6 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.testing.TestableLooper;
@@ -115,6 +116,7 @@
protected int mSubId;
private NetworkCapabilities mNetCapabilities;
+ private ConnectivityManager.NetworkCallback mDefaultNetworkCallback;
private ConnectivityManager.NetworkCallback mNetworkCallback;
@Rule
@@ -214,6 +216,10 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mMockCm, atLeastOnce())
.registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
+ mDefaultNetworkCallback = callbackArg.getValue();
+ assertNotNull(mDefaultNetworkCallback);
+ verify(mMockCm, atLeastOnce()).registerNetworkCallback(
+ isA(NetworkRequest.class), callbackArg.capture(), isA(Handler.class));
mNetworkCallback = callbackArg.getValue();
assertNotNull(mNetworkCallback);
}
@@ -270,10 +276,19 @@
}
public void setConnectivityViaCallback(
- int networkType, boolean validated, boolean isConnected){
+ int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+ mNetCapabilities.setTransportInfo(wifiInfo);
setConnectivityCommon(networkType, validated, isConnected);
- mNetworkCallback.onCapabilitiesChanged(
+ mDefaultNetworkCallback.onCapabilitiesChanged(
mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
+ if (isConnected) {
+ mNetworkCallback.onCapabilitiesChanged(
+ mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+ } else {
+ mNetworkCallback.onLost(mock(Network.class));
+ }
+ }
}
private void setConnectivityCommon(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 988e022..9c9d6ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -32,6 +32,7 @@
// These match the constants in WifiManager and need to be kept up to date.
private static final int MIN_RSSI = -100;
private static final int MAX_RSSI = -55;
+ private WifiInfo mWifiInfo = mock(WifiInfo.class);
@Test
public void testWifiIcon() {
@@ -41,15 +42,16 @@
setWifiState(true, testSsid);
setWifiLevel(0);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
// Connected, but still not validated - does not show
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
// Icon does not show if not validated
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@@ -69,10 +71,10 @@
for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
testSsid);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
testSsid);
}
@@ -86,7 +88,7 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastQsWifiIcon(true, true,
WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
@@ -111,14 +113,14 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setupDefaultSignal();
setGsmRoaming(true);
// Still be on wifi though.
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_CELLULAR, false, false, mWifiInfo);
verifyLastMobileDataIndicators(true,
DEFAULT_LEVEL,
0, true);
@@ -132,10 +134,10 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
}
@@ -147,11 +149,11 @@
setWifiEnabled(true);
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
setWifiState(false, testSsid);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, false);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, false, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
}
@@ -162,14 +164,14 @@
setWifiEnabled(true);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, false, true);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, false, true, mWifiInfo);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, true, true, mWifiInfo);
verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
// Mock calling setUnderlyingNetworks.
setWifiState(true, testSsid);
setWifiLevel(testLevel);
- setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+ setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
}
@@ -209,6 +211,7 @@
int rssi = (int)(MIN_RSSI + level * amountPerLevel);
// Put RSSI in the middle of the range.
rssi += amountPerLevel / 2;
+ when(mWifiInfo.getRssi()).thenReturn(rssi);
Intent i = new Intent(WifiManager.RSSI_CHANGED_ACTION);
i.putExtra(WifiManager.EXTRA_NEW_RSSI, rssi);
mNetworkController.onReceive(mContext, i);
@@ -224,10 +227,8 @@
Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
NetworkInfo networkInfo = mock(NetworkInfo.class);
when(networkInfo.isConnected()).thenReturn(connected);
-
- WifiInfo wifiInfo = mock(WifiInfo.class);
- when(wifiInfo.getSSID()).thenReturn(ssid);
- when(mMockWm.getConnectionInfo()).thenReturn(wifiInfo);
+ when(mWifiInfo.getSSID()).thenReturn(ssid);
+ when(mMockWm.getConnectionInfo()).thenReturn(mWifiInfo);
i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
mNetworkController.onReceive(mContext, i);
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
index a75fb2d..15f01e6 100644
--- a/packages/WAPPushManager/AndroidManifest.xml
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -27,7 +27,8 @@
<original-package android:name="com.android.smspush" />
<application
- android:allowClearUserData="false">
+ android:allowClearUserData="false"
+ android:directBootAware="true">
<service android:name=".WapPushManager"
android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
android:exported="true">
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
index dc2707b..951e64f 100755
--- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -26,17 +26,21 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
-import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IWapPushManager;
import com.android.internal.telephony.WapPushManagerParams;
+import java.io.File;
+
/**
* The WapPushManager service is implemented to process incoming
* WAP Push messages and to maintain the Receiver Application/Application
@@ -67,8 +71,13 @@
/**
* Inner class that deals with application ID table
*/
- private class WapPushManDBHelper extends SQLiteOpenHelper {
- WapPushManDBHelper(Context context) {
+ @VisibleForTesting
+ public static class WapPushManDBHelper extends SQLiteOpenHelper {
+ /**
+ * Constructor
+ */
+ @VisibleForTesting
+ public WapPushManDBHelper(Context context) {
super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
}
@@ -269,10 +278,6 @@
int app_type, boolean need_signature, boolean further_processing) {
WapPushManDBHelper dbh = getDatabase(mContext);
SQLiteDatabase db = dbh.getWritableDatabase();
- WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
- boolean ret = false;
- boolean insert = false;
- int sq = 0;
if (!appTypeCheck(app_type)) {
Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
@@ -280,34 +285,8 @@
+ WapPushManagerParams.APP_TYPE_SERVICE);
return false;
}
-
- if (lastapp == null) {
- insert = true;
- sq = 0;
- } else if (!lastapp.packageName.equals(package_name) ||
- !lastapp.className.equals(class_name)) {
- insert = true;
- sq = lastapp.installOrder + 1;
- }
-
- if (insert) {
- ContentValues values = new ContentValues();
-
- values.put("x_wap_application", x_app_id);
- values.put("content_type", content_type);
- values.put("package_name", package_name);
- values.put("class_name", class_name);
- values.put("app_type", app_type);
- values.put("need_signature", need_signature ? 1 : 0);
- values.put("further_processing", further_processing ? 1 : 0);
- values.put("install_order", sq);
- db.insert(APPID_TABLE_NAME, null, values);
- if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
- + " " + package_name + "." + class_name
- + ", newsq:" + sq);
- ret = true;
- }
-
+ boolean ret = insertPackage(dbh, db, x_app_id, content_type, package_name, class_name,
+ app_type, need_signature, further_processing);
db.close();
return ret;
@@ -404,11 +383,91 @@
protected WapPushManDBHelper getDatabase(Context context) {
if (mDbHelper == null) {
if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
- mDbHelper = new WapPushManDBHelper(context);
+ mDbHelper = new WapPushManDBHelper(context.createDeviceProtectedStorageContext());
}
+ // Migrate existing legacy database into the device encrypted storage.
+ migrateWapPushManDBIfNeeded(context);
return mDbHelper;
}
+ /**
+ * Inserts a package information into a database
+ */
+ @VisibleForTesting
+ public boolean insertPackage(WapPushManDBHelper dbh, SQLiteDatabase db, String appId,
+ String contentType, String packageName, String className, int appType,
+ boolean needSignature, boolean furtherProcessing) {
+
+ WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, appId, contentType);
+ boolean insert = false;
+ int sq = 0;
+
+ if (lastapp == null) {
+ insert = true;
+ sq = 0;
+ } else if (!lastapp.packageName.equals(packageName)
+ || !lastapp.className.equals(className)) {
+ insert = true;
+ sq = lastapp.installOrder + 1;
+ }
+
+ if (insert) {
+ ContentValues values = new ContentValues();
+
+ values.put("x_wap_application", appId);
+ values.put("content_type", contentType);
+ values.put("package_name", packageName);
+ values.put("class_name", className);
+ values.put("app_type", appType);
+ values.put("need_signature", needSignature ? 1 : 0);
+ values.put("further_processing", furtherProcessing ? 1 : 0);
+ values.put("install_order", sq);
+ db.insert(APPID_TABLE_NAME, null, values);
+ if (LOCAL_LOGV) {
+ Log.v(LOG_TAG, "add:" + appId + ":" + contentType + " " + packageName
+ + "." + className + ", newsq:" + sq);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Migrates a legacy database into the device encrypted storage
+ */
+ private void migrateWapPushManDBIfNeeded(Context context) {
+ UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ File file = context.getDatabasePath(DATABASE_NAME);
+ if (!userManager.isUserUnlocked() || !file.exists()) {
+ // Check if the device is unlocked because a legacy database can't access during
+ // DirectBoot.
+ return;
+ }
+
+ // Migration steps below:
+ // 1. Merge the package info to legacy database if there is any package info which is
+ // registered during DirectBoot.
+ // 2. Move the data base to the device encryped storage.
+ WapPushManDBHelper legacyDbHelper = new WapPushManDBHelper(context);
+ SQLiteDatabase legacyDb = legacyDbHelper.getWritableDatabase();
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ Cursor cur = db.query(APPID_TABLE_NAME, null, null, null, null, null, null);
+ while (cur.moveToNext()) {
+ insertPackage(legacyDbHelper, legacyDb,
+ cur.getString(cur.getColumnIndex("x_wap_application")),
+ cur.getString(cur.getColumnIndex("content_type")),
+ cur.getString(cur.getColumnIndex("package_name")),
+ cur.getString(cur.getColumnIndex("class_name")),
+ cur.getInt(cur.getColumnIndex("app_type")),
+ cur.getInt(cur.getColumnIndex("need_signature")) == 1,
+ cur.getInt(cur.getColumnIndex("further_processing")) == 1);
+ }
+ cur.close();
+ legacyDb.close();
+ db.close();
+ context.createDeviceProtectedStorageContext().moveDatabaseFrom(context, DATABASE_NAME);
+ Log.i(LOG_TAG, "Migrated the legacy database.");
+ }
/**
* This method is used for testing
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
index f7afc57..b9dac4e 100644
--- a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.database.sqlite.SQLiteDatabase;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Telephony.Sms.Intents;
@@ -33,7 +34,9 @@
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.HexDump;
import com.android.smspush.WapPushManager;
+import com.android.smspush.WapPushManager.WapPushManDBHelper;
+import java.io.File;
import java.util.Random;
/**
@@ -467,8 +470,9 @@
try {
super.setUp();
// get verifier
- getContext().bindService(new Intent(IDataVerify.class.getName()),
- mConn, Context.BIND_AUTO_CREATE);
+ Intent intent = new Intent(IDataVerify.class.getName());
+ intent.setPackage("com.android.smspush.unitTests");
+ getContext().bindService(intent, mConn, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
Log.w(LOG_TAG, "super exception");
}
@@ -552,15 +556,15 @@
}
/**
- * Add sqlite injection test
+ * Sqlite injection test
*/
- public void testAddPackage0() {
+ public void testSqliteInjection() {
String inject = "' union select 0,'com.android.settings','com.android.settings.Settings',0,0,0--";
- // insert new data
+ // update data
IWapPushManager iwapman = getInterface();
try {
- assertFalse(iwapman.addPackage(
+ assertFalse(iwapman.updatePackage(
inject,
Integer.toString(mContentTypeValue),
mPackageName, mClassName,
@@ -2528,4 +2532,45 @@
mMessageBody = originalMessageBody;
}
+ /**
+ * DataBase migration test.
+ */
+ public void testDataBaseMigration() {
+ IWapPushManager iwapman = getInterface();
+ WapPushManager wpman = getService();
+ Context context = getContext();
+
+ addPackageToLegacyDB(mAppIdValue, mContentTypeValue, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, true, true);
+ addPackageToLegacyDB(mAppIdValue + 10, mContentTypeValue, mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, true, true);
+
+ File oldDbFile = context.getDatabasePath("wappush.db");
+ assertTrue(oldDbFile.exists());
+ assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue),
+ mPackageName, mClassName,
+ WapPushManagerParams.APP_TYPE_SERVICE, true, true));
+ assertFalse(oldDbFile.exists());
+
+ // Clean up DB
+ try {
+ iwapman.deletePackage(Integer.toString(mAppIdValue),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+ Integer.toString(mContentTypeValue), mPackageName, mClassName);
+ } catch (RemoteException e) {
+ assertTrue(false);
+ }
+ }
+
+ private void addPackageToLegacyDB(int appId, int contextType, String packagename,
+ String classnName, int appType, boolean signature, boolean furtherProcessing) {
+ WapPushManager wpman = getService();
+ WapPushManDBHelper dbh = new WapPushManDBHelper(getContext());
+ SQLiteDatabase db = dbh.getWritableDatabase();
+
+ wpman.insertPackage(dbh, db, Integer.toString(appId), Integer.toString(contextType),
+ packagename, classnName, appType, signature, furtherProcessing);
+ }
}
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index ba2a63a..cc36b47 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -9,4 +9,3 @@
niamhfw@google.com
philippov@google.com
rthakohov@google.com
-tobiast@google.com
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 70c9fe7..629006a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1281,14 +1281,22 @@
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+ mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
// Listen for user add/removes to inform PermissionMonitor.
// Should run on mHandler to avoid any races.
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_ADDED);
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ final IntentFilter userIntentFilter = new IntentFilter();
+ userIntentFilter.addAction(Intent.ACTION_USER_ADDED);
+ userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ mUserAllContext.registerReceiver(mUserIntentReceiver, userIntentFilter,
+ null /* broadcastPermission */, mHandler);
- mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
- mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
+ // Listen to package add/removes for netd
+ final IntentFilter packageIntentFilter = new IntentFilter();
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ packageIntentFilter.addDataScheme("package");
+ mUserAllContext.registerReceiver(mPackageIntentReceiver, packageIntentFilter,
null /* broadcastPermission */, mHandler);
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNetd);
@@ -1895,6 +1903,10 @@
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
newNc.setAdministratorUids(new int[0]);
+ if (!checkAnyPermissionOf(
+ callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
+ newNc.setSubIds(Collections.emptySet());
+ }
return newNc;
}
@@ -4034,7 +4046,7 @@
}
}
}
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (null != nri.getActiveRequest()) {
@@ -4151,14 +4163,6 @@
? mSystemNetworkRequestCounter : mNetworkRequestCounter;
}
- private void incrementRequestCountOrThrow(NetworkRequestInfo nri) {
- getRequestCounter(nri).incrementCountOrThrow(nri.mUid);
- }
-
- private void decrementRequestCount(NetworkRequestInfo nri) {
- getRequestCounter(nri).decrementCount(nri.mUid);
- }
-
@Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceNetworkStackSettingsOrSetup();
@@ -4962,7 +4966,7 @@
}
@Override
- public void setGlobalProxy(final ProxyInfo proxyProperties) {
+ public void setGlobalProxy(@Nullable final ProxyInfo proxyProperties) {
PermissionUtils.enforceNetworkStackPermission(mContext);
mProxyTracker.setGlobalProxy(proxyProperties);
}
@@ -5307,14 +5311,14 @@
}
}
- private void onUserAdded(UserHandle user) {
+ private void onUserAdded(@NonNull final UserHandle user) {
mPermissionMonitor.onUserAdded(user);
if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
handleSetOemNetworkPreference(mOemNetworkPreferences, null);
}
}
- private void onUserRemoved(UserHandle user) {
+ private void onUserRemoved(@NonNull final UserHandle user) {
mPermissionMonitor.onUserRemoved(user);
// If there was a network preference for this user, remove it.
handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
@@ -5324,7 +5328,18 @@
}
}
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private void onPackageChanged(@NonNull final String packageName) {
+ // This is necessary in case a package is added or removed, but also when it's replaced to
+ // run as a new UID by its manifest rules. Also, if a separate package shares the same UID
+ // as one in the preferences, then it should follow the same routing as that other package,
+ // which means updating the rules is never to be needed in this case (whether it joins or
+ // leaves a UID with a preference).
+ if (isMappedInOemNetworkPreference(packageName)) {
+ handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+ }
+ }
+
+ private final BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ensureRunningOnConnectivityServiceThread();
@@ -5347,6 +5362,22 @@
}
};
+ private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ensureRunningOnConnectivityServiceThread();
+ switch (intent.getAction()) {
+ case Intent.ACTION_PACKAGE_ADDED:
+ case Intent.ACTION_PACKAGE_REMOVED:
+ case Intent.ACTION_PACKAGE_REPLACED:
+ onPackageChanged(intent.getData().getSchemeSpecificPart());
+ break;
+ default:
+ Log.wtf(TAG, "received unexpected intent: " + intent.getAction());
+ }
+ }
+ };
+
private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
@@ -5457,6 +5488,9 @@
@Nullable
final String mCallingAttributionTag;
+ // Counter keeping track of this NRI.
+ final PerUidCounter mPerUidCounter;
+
// Effective UID of this request. This is different from mUid when a privileged process
// files a request on behalf of another UID. This UID is used to determine blocked status,
// UID matching, and so on. mUid above is used for permission checks and to enforce the
@@ -5507,7 +5541,8 @@
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mAsUid = asUid;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
/**
* Location sensitive data not included in pending intent. Only included in
* {@link NetworkCallback}.
@@ -5539,7 +5574,8 @@
mUid = mDeps.getCallingUid();
mAsUid = asUid;
mPendingIntent = null;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = callbackFlags;
mCallingAttributionTag = callingAttributionTag;
linkDeathRecipient();
@@ -5577,7 +5613,8 @@
mUid = nri.mUid;
mAsUid = nri.mAsUid;
mPendingIntent = nri.mPendingIntent;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
linkDeathRecipient();
@@ -5609,6 +5646,10 @@
return Collections.unmodifiableList(tempRequests);
}
+ void decrementRequestCount() {
+ mPerUidCounter.decrementCount(mUid);
+ }
+
void linkDeathRecipient() {
if (null != mBinder) {
try {
@@ -5666,6 +5707,10 @@
"Insufficient permissions to request a specific signal strength");
}
mAppOpsManager.checkPackage(callerUid, callerPackageName);
+
+ if (!nc.getSubIds().isEmpty()) {
+ enforceNetworkFactoryPermission();
+ }
}
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
@@ -6156,6 +6201,15 @@
@NonNull
private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
+ /**
+ * Determine whether a given package has a mapping in the current OemNetworkPreferences.
+ * @param packageName the package name to check existence of a mapping for.
+ * @return true if a mapping exists, false otherwise
+ */
+ private boolean isMappedInOemNetworkPreference(@NonNull final String packageName) {
+ return mOemNetworkPreferences.getNetworkPreferences().containsKey(packageName);
+ }
+
// The always-on request for an Internet-capable network that apps without a specific default
// fall back to.
@VisibleForTesting
@@ -6176,7 +6230,7 @@
* @return the NetworkRequestInfo tracking the given uid.
*/
@NonNull
- private NetworkRequestInfo getDefaultRequestTrackingUid(@NonNull final int uid) {
+ private NetworkRequestInfo getDefaultRequestTrackingUid(final int uid) {
for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
if (nri == mDefaultRequest) {
continue;
@@ -6623,7 +6677,7 @@
mDeps.reportNetworkInterfaceForTransports(mContext, iface,
caps.getTransportTypes());
} catch (Exception e) {
- loge("Exception adding interface: " + e);
+ logw("Exception adding interface: " + e);
}
}
}
@@ -8773,7 +8827,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
return;
}
@@ -8839,7 +8893,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
iCb.unlinkToDeath(cbInfo, 0);
}
@@ -9415,14 +9469,8 @@
throw new IllegalArgumentException("Must explicitly specify a user handle ("
+ "UserHandle.CURRENT not supported)");
}
- final UserManager um;
- try {
- um = mContext.createContextAsUser(profile, 0 /* flags */)
- .getSystemService(UserManager.class);
- } catch (IllegalStateException e) {
- throw new IllegalArgumentException("Profile does not exist");
- }
- if (!um.isManagedProfile()) {
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile(profile.getIdentifier())) {
throw new IllegalArgumentException("Profile must be a managed profile");
}
// Strictly speaking, mOemNetworkPreferences should only be touched on the
@@ -9589,7 +9637,6 @@
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
replaceDefaultNetworkRequestsForPreference(nris);
mOemNetworkPreferences = preference;
- // TODO http://b/176496396 persist data to shared preferences.
if (null != listener) {
try {
@@ -9746,7 +9793,6 @@
// packages are sent on a network preference as the system will watch for
// package installations associated with this network preference and update
// accordingly. This is done so as to minimize race conditions on app install.
- // TODO b/177092163 add app install watching.
continue;
}
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 8eefbfc..f7ae58c 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
@@ -35,12 +37,13 @@
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
-import android.net.vcn.VcnManager;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnManager.VcnStatusCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
@@ -162,6 +165,9 @@
@NonNull private final VcnContext mVcnContext;
@NonNull private final BroadcastReceiver mPkgChangeReceiver;
+ @NonNull
+ private final TrackingNetworkCallback mTrackingNetworkCallback = new TrackingNetworkCallback();
+
/** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
@Nullable private LocationPermissionChecker mLocationPermissionChecker;
@@ -358,6 +364,10 @@
public void systemReady() {
mContext.getSystemService(ConnectivityManager.class)
.registerNetworkProvider(mNetworkProvider);
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
mTelephonySubscriptionTracker.register();
mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
}
@@ -532,15 +542,7 @@
if (mVcns.containsKey(subscriptionGroup)) {
final Vcn vcn = mVcns.get(subscriptionGroup);
- final boolean isActive = vcn.isActive();
vcn.updateConfig(config);
-
- // Only notify VcnStatusCallbacks if this VCN was previously in Safe Mode
- if (!isActive) {
- // TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- subscriptionGroup, VCN_STATUS_CODE_ACTIVE);
- }
} else {
startVcnLocked(subscriptionGroup, config);
}
@@ -769,7 +771,7 @@
synchronized (mLock) {
final Vcn vcn = mVcns.get(subGrp);
if (vcn != null) {
- if (vcn.isActive()) {
+ if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) {
isVcnManagedNetwork = true;
}
@@ -792,8 +794,9 @@
NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
}
+ final NetworkCapabilities result = ncBuilder.build();
return new VcnUnderlyingNetworkPolicy(
- false /* isTearDownRequested */, ncBuilder.build());
+ mTrackingNetworkCallback.requiresRestartForCarrierWifi(result), result);
});
}
@@ -880,20 +883,23 @@
// now that callback is registered, send it the VCN's current status
final VcnConfig vcnConfig = mConfigs.get(subGroup);
final Vcn vcn = mVcns.get(subGroup);
- final int vcnStatus;
+ final int vcnStatus =
+ vcn == null ? VCN_STATUS_CODE_NOT_CONFIGURED : vcn.getStatus();
+ final int resultStatus;
if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) {
- vcnStatus = VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
+ resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED;
} else if (vcn == null) {
- vcnStatus = VcnManager.VCN_STATUS_CODE_INACTIVE;
- } else if (vcn.isActive()) {
- vcnStatus = VcnManager.VCN_STATUS_CODE_ACTIVE;
+ resultStatus = VCN_STATUS_CODE_INACTIVE;
+ } else if (vcnStatus == VCN_STATUS_CODE_ACTIVE
+ || vcnStatus == VCN_STATUS_CODE_SAFE_MODE) {
+ resultStatus = vcnStatus;
} else {
- // TODO(b/181789060): create Vcn.getStatus() and Log.WTF() for unknown status
- vcnStatus = VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+ Slog.wtf(TAG, "Unknown VCN status: " + vcnStatus);
+ resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED;
}
try {
- cbInfo.mCallback.onVcnStatusChanged(vcnStatus);
+ cbInfo.mCallback.onVcnStatusChanged(resultStatus);
} catch (RemoteException e) {
Slog.d(TAG, "VcnStatusCallback threw on VCN status change", e);
}
@@ -926,17 +932,60 @@
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
/** Callback for Vcn signals sent up to VcnManagementService. */
public interface VcnCallback {
- /** Called by a Vcn to signal that it has entered safe mode. */
- void onEnteredSafeMode();
+ /** Called by a Vcn to signal that its safe mode status has changed. */
+ void onSafeModeStatusChanged(boolean isInSafeMode);
/** Called by a Vcn to signal that an error occurred. */
void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
}
+ /**
+ * TrackingNetworkCallback tracks all active networks
+ *
+ * <p>This is used to ensure that no underlying networks have immutable capabilities changed
+ * without requiring a Network restart.
+ */
+ private class TrackingNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private final Map<Network, NetworkCapabilities> mCaps = new ArrayMap<>();
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
+ synchronized (mCaps) {
+ mCaps.put(network, caps);
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ synchronized (mCaps) {
+ mCaps.remove(network);
+ }
+ }
+
+ private boolean requiresRestartForCarrierWifi(NetworkCapabilities caps) {
+ if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubIds() == null) {
+ return false;
+ }
+
+ synchronized (mCaps) {
+ for (NetworkCapabilities existing : mCaps.values()) {
+ if (existing.hasTransport(TRANSPORT_WIFI)
+ && caps.getSubIds().equals(existing.getSubIds())) {
+ // Restart if any immutable capabilities have changed
+ return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ }
+ }
+ }
+
+ return false;
+ }
+ }
+
/** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */
private class VcnCallbackImpl implements VcnCallback {
@NonNull private final ParcelUuid mSubGroup;
@@ -946,21 +995,24 @@
}
@Override
- public void onEnteredSafeMode() {
+ public void onSafeModeStatusChanged(boolean isInSafeMode) {
synchronized (mLock) {
// Ignore if this subscription group doesn't exist anymore
if (!mVcns.containsKey(mSubGroup)) {
return;
}
+ final int status =
+ isInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
+
notifyAllPolicyListenersLocked();
- notifyAllPermissionedStatusCallbacksLocked(mSubGroup, VCN_STATUS_CODE_SAFE_MODE);
+ notifyAllPermissionedStatusCallbacksLocked(mSubGroup, status);
}
}
@Override
public void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage) {
@@ -976,7 +1028,7 @@
Binder.withCleanCallingIdentity(
() ->
cbInfo.mCallback.onGatewayConnectionError(
- networkCapabilities,
+ gatewayConnectionName,
errorCode,
exceptionClass,
exceptionMessage));
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index 5e6b9f3..2e51be3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -36,7 +36,7 @@
import android.util.Pair;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.TrafficStatsConstants;
+import com.android.net.module.util.NetworkStackConstants;
import libcore.io.IoUtils;
@@ -446,7 +446,7 @@
int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
throws ErrnoException, IOException {
final int oldTag = TrafficStats.getAndSetThreadStatsTag(
- TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ NetworkStackConstants.TAG_SYSTEM_PROBE);
try {
mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
} finally {
@@ -745,7 +745,7 @@
if (ensureMeasurementNecessary()) return;
// No need to restore the tag, since this thread is only used for this measurement.
- TrafficStats.getAndSetThreadStatsTag(TrafficStatsConstants.TAG_SYSTEM_PROBE);
+ TrafficStats.getAndSetThreadStatsTag(NetworkStackConstants.TAG_SYSTEM_PROBE);
try (SSLSocket sslSocket = setupSSLSocket()) {
sendDoTProbe(sslSocket);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e5ce4f0..0a76bdc 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -101,7 +101,6 @@
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore2;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
@@ -463,7 +462,7 @@
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
loadAlwaysOnPackage();
@@ -527,7 +526,7 @@
private void resetNetworkCapabilities() {
mNetworkCapabilities = new NetworkCapabilities.Builder(mNetworkCapabilities)
.setUids(null)
- .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE))
+ .setTransportInfo(new VpnTransportInfo(VpnManager.TYPE_VPN_NONE, null))
.build();
}
@@ -1257,7 +1256,7 @@
capsBuilder.setUids(createUserAndRestrictedProfilesRanges(mUserId,
mConfig.allowedApplications, mConfig.disallowedApplications));
- capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ capsBuilder.setTransportInfo(new VpnTransportInfo(getActiveVpnType(), mConfig.session));
// Only apps targeting Q and above can explicitly declare themselves as metered.
// These VPNs are assumed metered unless they state otherwise.
@@ -2055,10 +2054,6 @@
if (alias == null) {
return null;
}
- // If Keystore 2.0 is not enabled the legacy private key prefix is used.
- if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
- return Credentials.USER_PRIVATE_KEY + alias;
- }
final KeyStore2 keystore2 = KeyStore2.getInstance();
KeyDescriptor key = new KeyDescriptor();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0215188..f96fa09 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -99,6 +99,7 @@
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.keystore2.AndroidKeyStoreProvider;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -262,7 +263,7 @@
@Override
public void onStart() {
- android.security.keystore2.AndroidKeyStoreProvider.install();
+ AndroidKeyStoreProvider.install();
mLockSettingsService = new LockSettingsService(getContext());
publishBinderService("lock_settings", mLockSettingsService);
}
@@ -796,10 +797,6 @@
// Notify keystore that a new user was added.
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
AndroidKeyStoreMaintenance.onUserAdded(userHandle);
- final KeyStore ks = KeyStore.getInstance();
- final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
- final int parentHandle = parentInfo != null ? parentInfo.id : -1;
- ks.onUserAdded(userHandle, parentHandle);
} else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
@@ -1264,19 +1261,11 @@
private void setKeystorePassword(byte[] password, int userHandle) {
AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
- final KeyStore ks = KeyStore.getInstance();
- // TODO(b/120484642): Update keystore to accept byte[] passwords
- String passwordString = password == null ? null : new String(password);
- ks.onUserPasswordChanged(userHandle, passwordString);
}
private void unlockKeystore(byte[] password, int userHandle) {
if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
Authorization.onLockScreenEvent(false, userHandle, password);
- // TODO(b/120484642): Update keystore to accept byte[] passwords
- String passwordString = password == null ? null : new String(password);
- final KeyStore ks = KeyStore.getInstance();
- ks.unlock(userHandle, passwordString);
}
@VisibleForTesting /** Note: this method is overridden in unit tests */
@@ -2296,8 +2285,6 @@
mStrongAuth.removeUser(userId);
AndroidKeyStoreMaintenance.onUserRemoved(userId);
- final KeyStore ks = KeyStore.getInstance();
- ks.onUserRemoved(userId);
mManagedProfilePasswordCache.removePassword(userId);
gateKeeperClearSecureUserId(userId);
diff --git a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
index 7950fcf..fa477c8 100644
--- a/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
+++ b/services/core/java/com/android/server/locksettings/ManagedProfilePasswordCache.java
@@ -20,10 +20,10 @@
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
-import android.security.keystore.AndroidKeyStoreSpi;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
+import android.security.keystore2.AndroidKeyStoreSpi;
import android.util.Slog;
import android.util.SparseArray;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
index bae029c..da29368 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowKeyStoreManager.java
@@ -16,11 +16,10 @@
package com.android.server.locksettings;
-import android.security.keystore.AndroidKeyStoreSpi;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
-import android.security.keystore2.AndroidKeyStoreProvider;
+import android.security.keystore2.AndroidKeyStoreSpi;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -67,9 +66,7 @@
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- if (AndroidKeyStoreProvider.isInstalled()) {
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
- }
+ loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
return (SecretKey) keyStore.getKey(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME,
null);
@@ -91,9 +88,7 @@
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
KeyStore.LoadStoreParameter loadStoreParameter = null;
// Load from the specific namespace if keystore2 is enabled.
- if (AndroidKeyStoreProvider.isInstalled()) {
- loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
- }
+ loadStoreParameter = new AndroidKeyStoreLoadStoreParameter(KEY_STORE_NAMESPACE);
keyStore.load(loadStoreParameter);
keyStore.deleteEntry(REBOOT_ESCROW_KEY_STORE_ENCRYPTION_KEY_NAME);
} catch (IOException | GeneralSecurityException e) {
@@ -119,9 +114,7 @@
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE);
// Generate the key with the correct namespace if keystore2 is enabled.
- if (AndroidKeyStoreProvider.isInstalled()) {
- parameterSpecBuilder.setNamespace(KEY_STORE_NAMESPACE);
- }
+ parameterSpecBuilder.setNamespace(KEY_STORE_NAMESPACE);
generator.init(parameterSpecBuilder.build());
return generator.generateKey();
} catch (GeneralSecurityException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
index 2398f56..8582c67 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/ApplicationKeyStorage.java
@@ -20,8 +20,6 @@
import android.annotation.Nullable;
import android.os.ServiceSpecificException;
-import android.security.Credentials;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
@@ -75,13 +73,7 @@
public @Nullable String getGrantAlias(int userId, int uid, String alias) {
Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
String keystoreAlias = getInternalAlias(userId, uid, alias);
- if (useKeyStore2()) {
- return makeKeystoreEngineGrantString(uid, keystoreAlias);
- } else {
- // Aliases used by {@link KeyStore} are different than used by public API.
- // {@code USER_PRIVATE_KEY} prefix is used secret keys.
- return KeyStore.getInstance().grant(Credentials.USER_PRIVATE_KEY + keystoreAlias, uid);
- }
+ return makeKeystoreEngineGrantString(uid, keystoreAlias);
}
public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
@@ -148,9 +140,4 @@
}
return String.format("%s%016X", APPLICATION_KEY_GRANT_PREFIX, key.nspace);
}
-
- private static boolean useKeyStore2() {
- return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
- }
-
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index c127858..290307b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5581,17 +5581,6 @@
}
@Override
- public boolean checkUidNetworkingBlocked(int uid, int uidRules,
- boolean isNetworkMetered, boolean isBackgroundRestricted) {
- mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
- // Log of invoking this function is disabled because it will be called very frequently. And
- // metrics are unlikely needed on this method because the callers are external and this
- // method doesn't take any locks or perform expensive operations.
- return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
- isBackgroundRestricted, null);
- }
-
- @Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index afcf81f..ff763fc 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -54,7 +54,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.security.Authorization;
-import android.security.KeyStore;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -701,13 +700,11 @@
dispatchDeviceLocked(userId, locked);
Authorization.onLockScreenEvent(locked, userId, null);
- KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
// Also update the user's profiles who have unified challenge, since they
// share the same unlocked state (see {@link #isDeviceLocked(int)})
for (int profileHandle : mUserManager.getEnabledProfileIds(userId)) {
if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(profileHandle)) {
mAuthorizationService.onLockScreenEvent(locked, profileHandle, null);
- KeyStore.getInstance().onUserLockedStateChanged(profileHandle, locked);
}
}
}
@@ -1259,7 +1256,6 @@
}
Authorization.onLockScreenEvent(locked, userId, null);
- KeyStore.getInstance().onUserLockedStateChanged(userId, locked);
if (locked) {
try {
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index 069c5c3..ab9de77 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -118,7 +118,7 @@
if (!mIsQuitting) {
mRouteSelectionCallback = new RouteSelectionCallback();
mConnectivityManager.requestBackgroundNetwork(
- getBaseNetworkRequestBuilder().build(), mHandler, mRouteSelectionCallback);
+ getRouteSelectionRequest(), mHandler, mRouteSelectionCallback);
mWifiBringupCallback = new NetworkBringupCallback();
mConnectivityManager.requestBackgroundNetwork(
@@ -149,12 +149,48 @@
}
}
- private NetworkRequest getWifiNetworkRequest() {
+ /**
+ * Builds the Route selection request
+ *
+ * <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue
+ * of a populated set of subIds as expressed in NetworkCapabilities#getSubIds(). Only carrier
+ * owned networks may be selected, as the request specifies only subIds in the VCN's
+ * subscription group, while the VCN networks are excluded by virtue of not having subIds set on
+ * the VCN-exposed networks.
+ */
+ private NetworkRequest getRouteSelectionRequest() {
return getBaseNetworkRequestBuilder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
.build();
}
+ /**
+ * Builds the WiFi bringup request
+ *
+ * <p>This request is built specifically to match only carrier-owned WiFi networks, but is also
+ * built to ONLY keep Carrier WiFi Networks alive (but never bring them up). This is a result of
+ * the WifiNetworkFactory not advertising a list of subIds, and therefore not accepting this
+ * request. As such, it will bind to a Carrier WiFi Network that has already been brought up,
+ * but will NEVER bring up a Carrier WiFi network itself.
+ */
+ private NetworkRequest getWifiNetworkRequest() {
+ return getBaseNetworkRequestBuilder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
+ .build();
+ }
+
+ /**
+ * Builds a Cellular bringup request for a given subId
+ *
+ * <p>This request is filed in order to ensure that the Telephony stack always has a
+ * NetworkRequest to bring up a VCN underlying cellular network. It is required in order to
+ * ensure that even when a VCN (appears as Cellular) satisfies the default request, Telephony
+ * will bring up additional underlying Cellular networks.
+ *
+ * <p>Since this request MUST make it to the TelephonyNetworkFactory, subIds are not specified
+ * in the NetworkCapabilities, but rather in the TelephonyNetworkSpecifier.
+ */
private NetworkRequest getCellNetworkRequestForSubId(int subId) {
return getBaseNetworkRequestBuilder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -164,20 +200,13 @@
/**
* Builds and returns a NetworkRequest builder common to all Underlying Network requests
- *
- * <p>This request is guaranteed to select carrier-owned, non-VCN underlying networks by virtue
- * of a populated set of subIds as expressed in NetworkCapabilities#getSubIds(). Only carrier
- * owned networks may be selected, as the request specifies only subIds in the VCN's
- * subscription group, while the VCN networks are excluded by virtue of not having subIds set on
- * the VCN-exposed networks.
*/
private NetworkRequest.Builder getBaseNetworkRequestBuilder() {
return new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .setSubIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup));
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
/**
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 89ed956..ae806aa 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -17,6 +17,10 @@
package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static com.android.server.VcnManagementService.VDBG;
@@ -44,7 +48,6 @@
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents an single instance of a VCN.
@@ -94,17 +97,21 @@
*/
private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3;
+ /**
+ * Triggers reevaluation of safe mode conditions.
+ *
+ * <p>Upon entering safe mode, the VCN will only provide gateway connections opportunistically,
+ * leaving the underlying networks marked as NOT_VCN_MANAGED.
+ *
+ * <p>Any VcnGatewayConnection in safe mode will result in the entire Vcn instance being put
+ * into safe mode. Upon receiving this message, the Vcn MUST query all VcnGatewayConnections to
+ * determine if any are in safe mode.
+ */
+ private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
- /**
- * Causes this VCN to immediately enter safe mode.
- *
- * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its
- * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode.
- */
- private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1;
-
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Dependencies mDeps;
@@ -137,17 +144,14 @@
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
/**
- * Whether this Vcn instance is active and running.
+ * The current status of this Vcn instance
*
- * <p>The value will be {@code true} while running. It will be {@code false} if the VCN has been
- * shut down or has entered safe mode.
- *
- * <p>This AtomicBoolean is required in order to ensure consistency and correctness across
- * multiple threads. Unlike the rest of the Vcn, this is queried synchronously on Binder threads
- * from VcnManagementService, and therefore cannot rely on guarantees of running on the VCN
- * Looper.
+ * <p>The value will be {@link VCN_STATUS_CODE_ACTIVE} while all VcnGatewayConnections are in
+ * good standing, {@link VCN_STATUS_CODE_SAFE_MODE} if any VcnGatewayConnections are in safe
+ * mode, and {@link VCN_STATUS_CODE_INACTIVE} once a teardown has been commanded.
*/
- private final AtomicBoolean mIsActive = new AtomicBoolean(true);
+ // Accessed from different threads, but always under lock in VcnManagementService
+ private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE;
public Vcn(
@NonNull VcnContext vcnContext,
@@ -199,9 +203,15 @@
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
}
- /** Synchronously checks whether this Vcn is active. */
- public boolean isActive() {
- return mIsActive.get();
+ /** Synchronously retrieves the current status code. */
+ public int getStatus() {
+ return mCurrentStatus;
+ }
+
+ /** Sets the status of this VCN */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public void setStatus(int status) {
+ mCurrentStatus = status;
}
/** Get current Gateways for testing purposes */
@@ -217,12 +227,6 @@
return Collections.unmodifiableMap(new HashMap<>(mVcnGatewayConnections));
}
- /** Set whether this Vcn is active for testing purposes */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public void setIsActive(boolean isActive) {
- mIsActive.set(isActive);
- }
-
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -234,6 +238,11 @@
@Override
public void handleMessage(@NonNull Message msg) {
+ if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE
+ && mCurrentStatus != VCN_STATUS_CODE_SAFE_MODE) {
+ return;
+ }
+
switch (msg.what) {
case MSG_EVENT_CONFIG_UPDATED:
handleConfigUpdated((VcnConfig) msg.obj);
@@ -247,12 +256,12 @@
case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
break;
+ case MSG_EVENT_SAFE_MODE_STATE_CHANGED:
+ handleSafeModeStatusChanged();
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
- case MSG_CMD_ENTER_SAFE_MODE:
- handleEnterSafeMode();
- break;
default:
Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
}
@@ -264,35 +273,28 @@
mConfig = config;
- if (mIsActive.getAndSet(true)) {
- // VCN is already active - teardown any GatewayConnections whose configs have been
- // removed and get all current requests
- for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
- mVcnGatewayConnections.entrySet()) {
- final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
- final VcnGatewayConnection gatewayConnection = entry.getValue();
+ // Teardown any GatewayConnections whose configs have been removed and get all current
+ // requests
+ for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
+ mVcnGatewayConnections.entrySet()) {
+ final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
+ final VcnGatewayConnection gatewayConnection = entry.getValue();
- // GatewayConnectionConfigs must match exactly (otherwise authentication or
- // connection details may have changed).
- if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
- if (gatewayConnection == null) {
- Slog.wtf(
- getLogTag(),
- "Found gatewayConnectionConfig without GatewayConnection");
- } else {
- gatewayConnection.teardownAsynchronously();
- }
+ // GatewayConnectionConfigs must match exactly (otherwise authentication or
+ // connection details may have changed).
+ if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
+ if (gatewayConnection == null) {
+ Slog.wtf(
+ getLogTag(), "Found gatewayConnectionConfig without GatewayConnection");
+ } else {
+ gatewayConnection.teardownAsynchronously();
}
}
-
- // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
- // satisfied start a new GatewayConnection)
- mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
- } else {
- // If this VCN was not previously active, it is exiting Safe Mode. Re-register the
- // request listener to get NetworkRequests again (and all cached requests).
- mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
+
+ // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
+ // satisfied start a new GatewayConnection)
+ mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
}
private void handleTeardown() {
@@ -302,22 +304,30 @@
gatewayConnection.teardownAsynchronously();
}
- mIsActive.set(false);
+ mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
}
- private void handleEnterSafeMode() {
- handleTeardown();
+ private void handleSafeModeStatusChanged() {
+ boolean hasSafeModeGatewayConnection = false;
- mVcnCallback.onEnteredSafeMode();
+ // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
+ for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+ if (gatewayConnection.isInSafeMode()) {
+ hasSafeModeGatewayConnection = true;
+ break;
+ }
+ }
+
+ final int oldStatus = mCurrentStatus;
+ mCurrentStatus =
+ hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
+ if (oldStatus != mCurrentStatus) {
+ mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
+ }
}
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
- if (!isActive()) {
- Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now");
- return;
- }
-
if (score > getNetworkScore()) {
if (VDBG) {
Slog.v(
@@ -370,25 +380,23 @@
mVcnGatewayConnections.remove(config);
// Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
- // start a new GatewayConnection), but only if the Vcn is still active
- if (isActive()) {
- mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
- }
+ // start a new GatewayConnection). VCN is always alive here, courtesy of the liveness check
+ // in handleMessage()
+ mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
}
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
- if (isActive()) {
- for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
- gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
- }
+ for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+ gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
}
private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
+ builder.addTransportType(TRANSPORT_CELLULAR);
builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
for (int cap : config.getAllExposedCapabilities()) {
builder.addCapability(cap);
@@ -412,12 +420,12 @@
/** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
@VisibleForTesting(visibility = Visibility.PACKAGE)
public interface VcnGatewayStatusCallback {
- /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
- void onEnteredSafeMode();
+ /** Called by a VcnGatewayConnection to indicate that it's safe mode status has changed. */
+ void onSafeModeStatusChanged();
/** Callback by a VcnGatewayConnection to indicate that an error occurred. */
void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage);
@@ -439,18 +447,18 @@
}
@Override
- public void onEnteredSafeMode() {
- sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
+ public void onSafeModeStatusChanged() {
+ sendMessage(obtainMessage(MSG_EVENT_SAFE_MODE_STATE_CHANGED));
}
@Override
public void onGatewayConnectionError(
- @NonNull int[] networkCapabilities,
+ @NonNull String gatewayConnectionName,
@VcnErrorCode int errorCode,
@Nullable String exceptionClass,
@Nullable String exceptionMessage) {
mVcnCallback.onGatewayConnectionError(
- networkCapabilities, errorCode, exceptionClass, exceptionMessage);
+ gatewayConnectionName, errorCode, exceptionClass, exceptionMessage);
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 9589505..20c08eb 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -44,6 +45,7 @@
import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier;
import android.net.Uri;
@@ -92,6 +94,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* A single VCN Gateway Connection, providing a single public-facing VCN network.
@@ -504,6 +507,15 @@
private boolean mIsQuitting = false;
/**
+ * Whether the VcnGatewayConnection is in safe mode.
+ *
+ * <p>Upon hitting the safe mode timeout, this will be set to {@code true}. In safe mode, this
+ * VcnGatewayConnection will continue attempting to connect, and if a successful connection is
+ * made, safe mode will be exited.
+ */
+ private boolean mIsInSafeMode = false;
+
+ /**
* The token used by the primary/current/active session.
*
* <p>This token MUST be updated when a new stateful/async session becomes the
@@ -562,8 +574,7 @@
* <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
* otherwise.
*/
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- NetworkAgent mNetworkAgent;
+ private NetworkAgent mNetworkAgent;
@Nullable private WakeupMessage mTeardownTimeoutAlarm;
@Nullable private WakeupMessage mDisconnectRequestAlarm;
@@ -628,6 +639,14 @@
start();
}
+ /** Queries whether this VcnGatewayConnection is in safe mode. */
+ public boolean isInSafeMode() {
+ // Accessing internal state; must only be done on looper thread.
+ mVcnContext.ensureRunningOnLooperThread();
+
+ return mIsInSafeMode;
+ }
+
/**
* Asynchronously tears down this GatewayConnection, and any resources used.
*
@@ -980,7 +999,7 @@
// IkeSessionCallback.onClosedExceptionally(), which calls sessionClosed()
if (exception != null) {
mGatewayStatusCallback.onGatewayConnectionError(
- mConnectionConfig.getExposedCapabilities(),
+ mConnectionConfig.getGatewayConnectionName(),
VCN_ERROR_CODE_INTERNAL_ERROR,
RuntimeException.class.getName(),
"Received "
@@ -1017,7 +1036,7 @@
}
mGatewayStatusCallback.onGatewayConnectionError(
- mConnectionConfig.getExposedCapabilities(),
+ mConnectionConfig.getGatewayConnectionName(),
errorCode,
exceptionClass,
exceptionMessage);
@@ -1162,6 +1181,15 @@
}
}
+ protected void handleSafeModeTimeoutExceeded() {
+ mSafeModeTimeoutAlarm = null;
+
+ // Connectivity for this GatewayConnection is broken; tear down the Network.
+ teardownNetwork();
+ mIsInSafeMode = true;
+ mGatewayStatusCallback.onSafeModeStatusChanged();
+ }
+
protected void logUnexpectedEvent(int what) {
Slog.d(TAG, String.format(
"Unexpected event code %d in state %s", what, this.getClass().getSimpleName()));
@@ -1315,8 +1343,7 @@
}
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
- mGatewayStatusCallback.onEnteredSafeMode();
- mSafeModeTimeoutAlarm = null;
+ handleSafeModeTimeoutExceeded();
break;
default:
logUnhandledMessage(msg);
@@ -1401,8 +1428,7 @@
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
- mGatewayStatusCallback.onEnteredSafeMode();
- mSafeModeTimeoutAlarm = null;
+ handleSafeModeTimeoutExceeded();
break;
default:
logUnhandledMessage(msg);
@@ -1432,30 +1458,35 @@
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+ final NetworkAgentConfig nac =
+ new NetworkAgentConfig.Builder()
+ .setLegacyType(ConnectivityManager.TYPE_MOBILE)
+ .build();
final NetworkAgent agent =
- new NetworkAgent(
- mVcnContext.getContext(),
- mVcnContext.getLooper(),
+ mDeps.newNetworkAgent(
+ mVcnContext,
TAG,
caps,
lp,
Vcn.getNetworkScore(),
- new NetworkAgentConfig.Builder().build(),
- mVcnContext.getVcnNetworkProvider()) {
- @Override
- public void onNetworkUnwanted() {
- Slog.d(TAG, "NetworkAgent was unwanted");
- teardownAsynchronously();
- }
-
- @Override
- public void onValidationStatus(int status, @Nullable Uri redirectUri) {
- if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
- clearFailedAttemptCounterAndSafeModeAlarm();
- }
- }
- };
+ nac,
+ mVcnContext.getVcnNetworkProvider(),
+ () -> {
+ Slog.d(TAG, "NetworkAgent was unwanted");
+ // If network agent has already been torn down, skip sending the
+ // disconnect. Unwanted() is always called, even when networkAgents
+ // are unregistered in teardownNetwork(), so prevent duplicate
+ // notifications.
+ if (mNetworkAgent != null) {
+ teardownAsynchronously();
+ }
+ } /* networkUnwantedCallback */,
+ (status) -> {
+ if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
+ clearFailedAttemptCounterAndSafeModeAlarm();
+ }
+ } /* validationStatusCallback */);
agent.register();
agent.markConnected();
@@ -1469,6 +1500,11 @@
// Validated connection, clear failed attempt counter
mFailedAttempts = 0;
cancelSafeModeAlarm();
+
+ if (mIsInSafeMode) {
+ mIsInSafeMode = false;
+ mGatewayStatusCallback.onSafeModeStatusChanged();
+ }
}
protected void applyTransform(
@@ -1491,13 +1527,6 @@
protected void setupInterface(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull VcnChildSessionConfiguration childConfig) {
- setupInterface(token, tunnelIface, childConfig, null);
- }
-
- protected void setupInterface(
- int token,
- @NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
@Nullable VcnChildSessionConfiguration oldChildConfig) {
try {
@@ -1579,16 +1608,17 @@
transformCreatedInfo.direction);
break;
case EVENT_SETUP_COMPLETED:
+ final VcnChildSessionConfiguration oldChildConfig = mChildConfig;
mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
- setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+ setupInterfaceAndNetworkAgent(
+ mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig);
break;
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
- mGatewayStatusCallback.onEnteredSafeMode();
- mSafeModeTimeoutAlarm = null;
+ handleSafeModeTimeoutExceeded();
break;
default:
logUnhandledMessage(msg);
@@ -1626,8 +1656,9 @@
protected void setupInterfaceAndNetworkAgent(
int token,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull VcnChildSessionConfiguration childConfig) {
- setupInterface(token, tunnelIface, childConfig);
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @NonNull VcnChildSessionConfiguration oldChildConfig) {
+ setupInterface(token, tunnelIface, childConfig, oldChildConfig);
if (mNetworkAgent == null) {
mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
@@ -1692,8 +1723,7 @@
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
break;
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
- mGatewayStatusCallback.onEnteredSafeMode();
- mSafeModeTimeoutAlarm = null;
+ handleSafeModeTimeoutExceeded();
break;
default:
logUnhandledMessage(msg);
@@ -1935,6 +1965,16 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ NetworkAgent getNetworkAgent() {
+ return mNetworkAgent;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setNetworkAgent(@Nullable NetworkAgent networkAgent) {
+ mNetworkAgent = networkAgent;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) {
sendMessageAndAcquireWakeLock(
EVENT_DISCONNECT_REQUESTED,
@@ -2018,6 +2058,38 @@
return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
}
+ /** Builds a new NetworkAgent. */
+ public NetworkAgent newNetworkAgent(
+ @NonNull VcnContext vcnContext,
+ @NonNull String tag,
+ @NonNull NetworkCapabilities caps,
+ @NonNull LinkProperties lp,
+ @NonNull int score,
+ @NonNull NetworkAgentConfig nac,
+ @NonNull NetworkProvider provider,
+ @NonNull Runnable networkUnwantedCallback,
+ @NonNull Consumer<Integer> validationStatusCallback) {
+ return new NetworkAgent(
+ vcnContext.getContext(),
+ vcnContext.getLooper(),
+ tag,
+ caps,
+ lp,
+ score,
+ nac,
+ provider) {
+ @Override
+ public void onNetworkUnwanted() {
+ networkUnwantedCallback.run();
+ }
+
+ @Override
+ public void onValidationStatus(int status, @Nullable Uri redirectUri) {
+ validationStatusCallback.accept(status);
+ }
+ };
+ }
+
/** Gets the elapsed real time since boot, in millis. */
public long getElapsedRealTime() {
return SystemClock.elapsedRealtime();
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index ea47a27..24bb1b1 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -110,7 +110,6 @@
"libhardware",
"libhardware_legacy",
"libhidlbase",
- "libkeystore_binder",
"libmtp",
"libnativehelper",
"libprocessgroup",
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 100d3ea..f5876fa 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -33,12 +33,6 @@
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
-import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
@@ -49,7 +43,6 @@
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.Process.SYSTEM_UID;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -141,7 +134,6 @@
import android.util.ArrayMap;
import android.util.DataUnit;
import android.util.Log;
-import android.util.Pair;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -1844,68 +1836,6 @@
reset(mStatsService);
}
- /**
- * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
- * conditions.
- */
- @Test
- public void testCheckUidNetworkingBlocked() {
- final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
-
- // Metered network. Data saver on.
- expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
- expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
- expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
- expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
- verifyNetworkBlockedState(
- true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
- expectedBlockedStates.clear();
-
- // Metered network. Data saver off.
- expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
- expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
- expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
- verifyNetworkBlockedState(
- true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
- expectedBlockedStates.clear();
-
- // Non-metered network. Data saver on.
- expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
- expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
- expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
- expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
- verifyNetworkBlockedState(
- false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
-
- // Non-metered network. Data saver off. The result is the same as previous case since
- // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
- verifyNetworkBlockedState(
- false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
- expectedBlockedStates.clear();
- }
-
- private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
- ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
-
- for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
- final boolean expectedResult = pair.first;
- final int rule = pair.second;
- assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
- expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
- metered, backgroundRestricted));
- assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
- mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
- backgroundRestricted));
- }
- }
-
private void enableRestrictedMode(boolean enable) throws Exception {
mService.mRestrictedNetworkingMode = enable;
mService.updateRestrictedModeAllowlistUL();
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
index a5e5ab01..ad57b91 100644
--- a/telephony/java/android/telephony/data/DataCallResponse.java
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -70,6 +70,7 @@
/** {@hide} */
@IntDef(prefix = "HANDOVER_FAILURE_MODE_", value = {
+ HANDOVER_FAILURE_MODE_UNKNOWN,
HANDOVER_FAILURE_MODE_LEGACY,
HANDOVER_FAILURE_MODE_DO_FALLBACK,
HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER,
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index ad6d73c..b529563 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -24,6 +24,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import com.android.internal.telephony.SipMessageParsingUtils;
@@ -60,14 +61,19 @@
*/
public SipMessage(@NonNull String startLine, @NonNull String headerSection,
@NonNull byte[] content) {
- if (startLine == null || headerSection == null || content == null) {
- throw new IllegalArgumentException("One or more null parameters entered");
- }
+ Objects.requireNonNull(startLine, "Required parameter is null: startLine");
+ Objects.requireNonNull(headerSection, "Required parameter is null: headerSection");
+ Objects.requireNonNull(content, "Required parameter is null: content");
+
mStartLine = startLine;
mHeaderSection = headerSection;
mContent = content;
mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection);
+ if (TextUtils.isEmpty(mViaBranchParam)) {
+ throw new IllegalArgumentException("header section MUST contain a branch parameter "
+ + "inside of the Via header.");
+ }
mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection);
}
@@ -107,11 +113,9 @@
/**
* @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section
- * 20.42 for more information on the Via header. If {@code null}, then there was either no
- * Via parameter found in this SIP message's headers or no branch parameter found in the
- * Via header.
+ * 20.42 for more information on the Via header.
*/
- public @Nullable String getViaBranchParameter() {
+ public @NonNull String getViaBranchParameter() {
return mViaBranchParam;
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 739946b..5c9ec53 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -28,8 +28,6 @@
import android.telephony.ims.SipDelegateManager;
import android.telephony.ims.SipMessage;
import android.telephony.ims.stub.SipDelegate;
-import android.text.TextUtils;
-import android.util.Log;
import java.util.ArrayList;
import java.util.Set;
@@ -187,11 +185,6 @@
private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
String transactionId = m.getViaBranchParameter();
- if (TextUtils.isEmpty(transactionId)) {
- Log.w(LOG_TAG, "failure to parse SipMessage.");
- throw new IllegalArgumentException("Malformed SipMessage, can not determine "
- + "transaction ID.");
- }
SipDelegate d = mDelegate;
if (d != null) {
mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason));
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 3cd2726..ad02fe5 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -28,7 +28,6 @@
import android.telephony.ims.stub.DelegateConnectionMessageCallback;
import android.telephony.ims.stub.DelegateConnectionStateCallback;
import android.telephony.ims.stub.SipDelegate;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -267,12 +266,6 @@
private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
String transactionId = m.getViaBranchParameter();
- if (TextUtils.isEmpty(transactionId)) {
- Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
- + "transaction ID.");
- throw new IllegalArgumentException("Could not send SipMessage due to malformed header");
- }
- mExecutor.execute(() ->
- mMessageCallback.onMessageSendFailure(transactionId, reason));
+ mExecutor.execute(() -> mMessageCallback.onMessageSendFailure(transactionId, reason));
}
}
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index b7a42ec..fee65f0 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
+import static android.net.NetworkCapabilities.REDACT_NONE;
+
import static com.android.testutils.ParcelUtils.assertParcelSane;
import static org.junit.Assert.assertEquals;
@@ -33,23 +36,33 @@
@Test
public void testParceling() {
- VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- assertParcelSane(v, 1 /* fieldCount */);
+ VpnTransportInfo v = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, "12345");
+ assertParcelSane(v, 2 /* fieldCount */);
}
@Test
public void testEqualsAndHashCode() {
- VpnTransportInfo v1 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v2 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE);
- VpnTransportInfo v3 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM);
- VpnTransportInfo v4 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY);
- VpnTransportInfo v5 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM);
+ String session1 = "12345";
+ String session2 = "6789";
+ VpnTransportInfo v11 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v12 = new VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, session1);
+ VpnTransportInfo v13 = new VpnTransportInfo(VpnManager.TYPE_VPN_PLATFORM, session1);
+ VpnTransportInfo v14 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session1);
+ VpnTransportInfo v15 = new VpnTransportInfo(VpnManager.TYPE_VPN_OEM, session1);
+ VpnTransportInfo v21 = new VpnTransportInfo(VpnManager.TYPE_VPN_LEGACY, session2);
- assertNotEquals(v1, v2);
- assertNotEquals(v3, v4);
- assertNotEquals(v4, v5);
+ VpnTransportInfo v31 = v11.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
+ VpnTransportInfo v32 = v13.makeCopy(REDACT_FOR_NETWORK_SETTINGS);
- assertEquals(v1, v3);
- assertEquals(v1.hashCode(), v3.hashCode());
+ assertNotEquals(v11, v12);
+ assertNotEquals(v13, v14);
+ assertNotEquals(v14, v15);
+ assertNotEquals(v14, v21);
+
+ assertEquals(v11, v13);
+ assertEquals(v31, v32);
+ assertEquals(v11.hashCode(), v13.hashCode());
+ assertEquals(REDACT_FOR_NETWORK_SETTINGS, v32.getApplicableRedactions());
+ assertEquals(session1, v15.makeCopy(REDACT_NONE).sessionId);
}
-}
\ No newline at end of file
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index ad93420..270941f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -18,8 +18,12 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_PACKAGE_REPLACED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
@@ -577,6 +581,7 @@
final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
.getSystemService(UserManager.class);
doReturn(value).when(umMock).isManagedProfile();
+ doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier()));
}
@Override
@@ -1228,10 +1233,12 @@
if (mAgentRegistered) throw new IllegalStateException("already registered");
updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
mConfig = new VpnConfig();
+ mConfig.session = "MySession12345";
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
- mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType()));
+ mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(),
+ mConfig.session));
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -2791,10 +2798,14 @@
}
private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception {
- final String myPackageName = mContext.getPackageName();
- when(mPackageManager.getPackageInfo(eq(myPackageName), eq(GET_PERMISSIONS)))
+ grantUsingBackgroundNetworksPermissionForUid(uid, mContext.getPackageName());
+ }
+
+ private void grantUsingBackgroundNetworksPermissionForUid(
+ final int uid, final String packageName) throws Exception {
+ when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
.thenReturn(buildPackageInfo(true, uid));
- mService.mPermissionMonitor.onPackageAdded(myPackageName, uid);
+ mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
@Test
@@ -10253,6 +10264,12 @@
.thenReturn(applicationInfo);
}
+ private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName)
+ throws Exception {
+ when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+ .thenThrow(new PackageManager.NameNotFoundException(packageName));
+ }
+
private void mockHasSystemFeature(@NonNull final String featureName,
@NonNull final boolean hasFeature) {
when(mPackageManager.hasSystemFeature(eq(featureName)))
@@ -10709,15 +10726,23 @@
@NonNull final UidRangeParcel[] uidRanges,
@NonNull final String testPackageName)
throws Exception {
- mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
-
// These tests work off a single UID therefore using 'start' is valid.
mockGetApplicationInfo(testPackageName, uidRanges[0].start);
+ setOemNetworkPreference(networkPrefToSetup, testPackageName);
+ }
+
+ private void setOemNetworkPreference(final int networkPrefToSetup,
+ @NonNull final String... testPackageNames)
+ throws Exception {
+ mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+
// Build OemNetworkPreferences object
- final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
- .addNetworkPreference(testPackageName, networkPrefToSetup)
- .build();
+ final OemNetworkPreferences.Builder builder = new OemNetworkPreferences.Builder();
+ for (final String packageName : testPackageNames) {
+ builder.addNetworkPreference(packageName, networkPrefToSetup);
+ }
+ final OemNetworkPreferences pref = builder.build();
// Act on ConnectivityService.setOemNetworkPreference()
final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
@@ -11316,8 +11341,7 @@
// Arrange PackageManager mocks
final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
final UidRangeParcel[] uidRangesSingleUser =
- toUidRangeStableParcels(
- uidRangesForUids(TEST_PACKAGE_UID));
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
final UidRangeParcel[] uidRangesBothUsers =
toUidRangeStableParcels(
uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
@@ -11364,6 +11388,84 @@
false /* shouldDestroyNetwork */);
}
+ @Test
+ public void testMultilayerForPackageChangesEvaluatesCorrectly()
+ throws Exception {
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final String packageScheme = "package:";
+
+ // Arrange PackageManager mocks
+ final String packageToInstall = "package.to.install";
+ final int packageToInstallUid = 81387;
+ final UidRangeParcel[] uidRangesSinglePackage =
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+ mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+ setOemNetworkPreference(networkPref, TEST_PACKAGE_NAME, packageToInstall);
+ grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid(), packageToInstall);
+
+ // Verify the starting state. No networks should be connected.
+ verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Test that we correctly add the expected values for installed packages.
+ setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+ verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ OEM_PREF_ANY_NET_ID, 0 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Set the system to recognize the package to be installed
+ mockGetApplicationInfo(packageToInstall, packageToInstallUid);
+ final UidRangeParcel[] uidRangesAllPackages =
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID, packageToInstallUid));
+
+ // Send a broadcast indicating a package was installed.
+ final Intent addedIntent = new Intent(ACTION_PACKAGE_ADDED);
+ addedIntent.setData(Uri.parse(packageScheme + packageToInstall));
+ processBroadcast(addedIntent);
+
+ // Test the single package is removed and the combined packages are added.
+ verifySetOemNetworkPreferenceForPreference(uidRangesAllPackages, uidRangesSinglePackage,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Set the system to no longer recognize the package to be installed
+ mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+
+ // Send a broadcast indicating a package was removed.
+ final Intent removedIntent = new Intent(ACTION_PACKAGE_REMOVED);
+ removedIntent.setData(Uri.parse(packageScheme + packageToInstall));
+ processBroadcast(removedIntent);
+
+ // Test the combined packages are removed and the single package is added.
+ verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, uidRangesAllPackages,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+
+ // Set the system to change the installed package's uid
+ final int replacedTestPackageUid = TEST_PACKAGE_UID + 1;
+ mockGetApplicationInfo(TEST_PACKAGE_NAME, replacedTestPackageUid);
+ final UidRangeParcel[] uidRangesReplacedPackage =
+ toUidRangeStableParcels(uidRangesForUids(replacedTestPackageUid));
+
+ // Send a broadcast indicating a package was replaced.
+ final Intent replacedIntent = new Intent(ACTION_PACKAGE_REPLACED);
+ replacedIntent.setData(Uri.parse(packageScheme + TEST_PACKAGE_NAME));
+ processBroadcast(replacedIntent);
+
+ // Test the original uid is removed and is replaced with the new uid.
+ verifySetOemNetworkPreferenceForPreference(uidRangesReplacedPackage, uidRangesSinglePackage,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+ false /* shouldDestroyNetwork */);
+ }
+
/**
* Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
* NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
@@ -12186,4 +12288,68 @@
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
}
+
+ @Test
+ public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setSubIds(Collections.singleton(Process.myUid()));
+
+ final NetworkCapabilities result =
+ mService.networkCapabilitiesRestrictedForCallerPermissions(
+ nc, Process.myPid(), Process.myUid());
+ assertTrue(result.getSubIds().isEmpty());
+ }
+
+ @Test
+ public void testSubIdsExistWithNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+
+ final Set<Integer> subIds = Collections.singleton(Process.myUid());
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setSubIds(subIds);
+
+ final NetworkCapabilities result =
+ mService.networkCapabilitiesRestrictedForCallerPermissions(
+ nc, Process.myPid(), Process.myUid());
+ assertEquals(subIds, result.getSubIds());
+ }
+
+ private NetworkRequest getRequestWithSubIds() {
+ return new NetworkRequest.Builder()
+ .setSubIds(Collections.singleton(Process.myUid()))
+ .build();
+ }
+
+ @Test
+ public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+ final NetworkCallback networkCallback1 = new NetworkCallback();
+ final NetworkCallback networkCallback2 = new NetworkCallback();
+
+ mCm.requestNetwork(getRequestWithSubIds(), networkCallback1);
+ mCm.requestNetwork(getRequestWithSubIds(), pendingIntent);
+ mCm.registerNetworkCallback(getRequestWithSubIds(), networkCallback2);
+
+ mCm.unregisterNetworkCallback(networkCallback1);
+ mCm.releaseNetworkRequest(pendingIntent);
+ mCm.unregisterNetworkCallback(networkCallback2);
+ }
+
+ @Test
+ public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+
+ final Class<SecurityException> expected = SecurityException.class;
+ assertThrows(
+ expected, () -> mCm.requestNetwork(getRequestWithSubIds(), new NetworkCallback()));
+ assertThrows(expected, () -> mCm.requestNetwork(getRequestWithSubIds(), pendingIntent));
+ assertThrows(
+ expected,
+ () -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback()));
+ }
}
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index c1ef350..7ac51b7 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -79,6 +79,18 @@
}
@Test
+ public void testBuilderRequiresUniqueGatewayConnectionNames() {
+ final VcnGatewayConnectionConfig config = VcnGatewayConnectionConfigTest.buildTestConfig();
+ try {
+ new VcnConfig.Builder(mContext)
+ .addGatewayConnectionConfig(config)
+ .addGatewayConnectionConfig(config);
+ fail("Expected exception due to duplicate gateway connection name");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
public void testBuilderAndGetters() {
final VcnConfig config = buildTestConfig(mContext);
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 8a0c923..4ee4d61 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
@@ -61,13 +62,20 @@
public static final VcnControlPlaneConfig CONTROL_PLANE_CONFIG =
VcnControlPlaneIkeConfigTest.buildTestConfig();
+ public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
+ private static int sGatewayConnectionConfigCount = 0;
+
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
}
private static VcnGatewayConnectionConfig.Builder newBuilder() {
- return new VcnGatewayConnectionConfig.Builder(CONTROL_PLANE_CONFIG);
+ // Append a unique identifier to the name prefix to guarantee that all created
+ // VcnGatewayConnectionConfigs have a unique name (required by VcnConfig).
+ return new VcnGatewayConnectionConfig.Builder(
+ GATEWAY_CONNECTION_NAME_PREFIX + sGatewayConnectionConfigCount++,
+ CONTROL_PLANE_CONFIG);
}
// Public for use in VcnGatewayConnectionTest
@@ -87,9 +95,23 @@
}
@Test
+ public void testBuilderRequiresNonNullGatewayConnectionName() {
+ try {
+ new VcnGatewayConnectionConfig.Builder(
+ null /* gatewayConnectionName */, CONTROL_PLANE_CONFIG)
+ .build();
+
+ fail("Expected exception due to invalid gateway connection name");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonNullControlPlaneConfig() {
try {
- new VcnGatewayConnectionConfig.Builder(null).build();
+ new VcnGatewayConnectionConfig.Builder(
+ GATEWAY_CONNECTION_NAME_PREFIX, null /* ctrlPlaneConfig */)
+ .build();
fail("Expected exception due to invalid control plane config");
} catch (NullPointerException e) {
@@ -139,6 +161,8 @@
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
+ assertTrue(config.getGatewayConnectionName().startsWith(GATEWAY_CONNECTION_NAME_PREFIX));
+
int[] exposedCaps = config.getExposedCapabilities();
Arrays.sort(exposedCaps);
assertArrayEquals(EXPOSED_CAPS, exposedCaps);
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 516c206..8461de6 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -50,9 +50,7 @@
public class VcnManagerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
- private static final int[] UNDERLYING_NETWORK_CAPABILITIES = {
- NetworkCapabilities.NET_CAPABILITY_IMS, NetworkCapabilities.NET_CAPABILITY_INTERNET
- };
+ private static final String GATEWAY_CONNECTION_NAME = "gatewayConnectionName";
private static final Executor INLINE_EXECUTOR = Runnable::run;
private IVcnManagementService mMockVcnManagementService;
@@ -207,13 +205,13 @@
verify(mMockStatusCallback).onStatusChanged(VCN_STATUS_CODE_ACTIVE);
cbBinder.onGatewayConnectionError(
- UNDERLYING_NETWORK_CAPABILITIES,
+ GATEWAY_CONNECTION_NAME,
VcnManager.VCN_ERROR_CODE_NETWORK_ERROR,
UnknownHostException.class.getName(),
"exception_message");
verify(mMockStatusCallback)
.onGatewayConnectionError(
- eq(UNDERLYING_NETWORK_CAPABILITIES),
+ eq(GATEWAY_CONNECTION_NAME),
eq(VcnManager.VCN_ERROR_CODE_NETWORK_ERROR),
any(UnknownHostException.class));
}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index c88b0c1..43e6676 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,9 +16,13 @@
package com.android.server;
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -53,8 +57,10 @@
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities.Transport;
+import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -256,6 +262,10 @@
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
+ verify(mConnMgr)
+ .registerNetworkCallback(
+ eq(new NetworkRequest.Builder().clearCapabilities().build()),
+ any(NetworkCallback.class));
}
@Test
@@ -527,17 +537,6 @@
}
@Test
- public void testSetVcnConfigInSafeModeNotifiesStatusCallback() throws Exception {
- setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */);
- mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
- verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
-
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
-
- verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
- }
-
- @Test
public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
@@ -695,17 +694,17 @@
hasCarrierPrivileges);
final Vcn vcn = startAndGetVcnInstance(subGrp);
- doReturn(isVcnActive).when(vcn).isActive();
+ doReturn(isVcnActive ? VCN_STATUS_CODE_ACTIVE : VCN_STATUS_CODE_SAFE_MODE)
+ .when(vcn)
+ .getStatus();
doReturn(true)
.when(mLocationPermissionChecker)
.checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
}
- private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
- int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
- setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
-
+ private NetworkCapabilities.Builder getNetworkCapabilitiesBuilderForTransport(
+ int subId, int transport) {
final NetworkCapabilities.Builder ncBuilder =
new NetworkCapabilities.Builder()
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
@@ -714,7 +713,16 @@
ncBuilder.setSubIds(Collections.singleton(subId));
}
- return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties());
+ return ncBuilder;
+ }
+
+ private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
+ int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
+ setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
+
+ return mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+ getNetworkCapabilitiesBuilderForTransport(subId, transport).build(),
+ new LinkProperties());
}
@Test
@@ -776,6 +784,53 @@
true /* isRestricted */);
}
+ private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
+ mVcnMgmtSvc.systemReady();
+
+ final ArgumentCaptor<NetworkCallback> captor =
+ ArgumentCaptor.forClass(NetworkCallback.class);
+ verify(mConnMgr)
+ .registerNetworkCallback(
+ eq(new NetworkRequest.Builder().clearCapabilities().build()),
+ captor.capture());
+ captor.getValue().onCapabilitiesChanged(new Network(0), caps);
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyVcnWifi_unrestrictingExistingNetworkRequiresRestart()
+ throws Exception {
+ final NetworkCapabilities existingNetworkCaps =
+ getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build();
+ setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+
+ // Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED
+ // immutable capability
+ final VcnUnderlyingNetworkPolicy policy =
+ mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+ getNetworkCapabilitiesBuilderForTransport(
+ TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+ .build(),
+ new LinkProperties());
+ assertTrue(policy.isTeardownRequested());
+ }
+
+ @Test
+ public void testGetUnderlyingNetworkPolicyVcnWifi_restrictingExistingNetworkRequiresRestart()
+ throws Exception {
+ final NetworkCapabilities existingNetworkCaps =
+ getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+ .build();
+ setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+
+ final VcnUnderlyingNetworkPolicy policy =
+ startVcnAndGetPolicyForTransport(
+ TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
+
+ assertTrue(policy.isTeardownRequested());
+ }
+
@Test
public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */);
@@ -836,7 +891,9 @@
}
private void triggerVcnSafeMode(
- @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
+ @NonNull ParcelUuid subGroup,
+ @NonNull TelephonySubscriptionSnapshot snapshot,
+ boolean isInSafeMode)
throws Exception {
verify(mMockDeps)
.newVcn(
@@ -847,22 +904,32 @@
mVcnCallbackCaptor.capture());
VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
- vcnCallback.onEnteredSafeMode();
+ vcnCallback.onSafeModeStatusChanged(isInSafeMode);
}
- @Test
- public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
+ private void verifyVcnSafeModeChangesNotifiesPolicyListeners(boolean enterSafeMode)
+ throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
- triggerVcnSafeMode(TEST_UUID_1, snapshot);
+ triggerVcnSafeMode(TEST_UUID_1, snapshot, enterSafeMode);
verify(mMockPolicyListener).onPolicyChanged();
}
- private void triggerVcnStatusCallbackOnEnteredSafeMode(
+ @Test
+ public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
+ verifyVcnSafeModeChangesNotifiesPolicyListeners(true /* enterSafeMode */);
+ }
+
+ @Test
+ public void testVcnExitingSafeModeNotifiesPolicyListeners() throws Exception {
+ verifyVcnSafeModeChangesNotifiesPolicyListeners(false /* enterSafeMode */);
+ }
+
+ private void triggerVcnStatusCallbackOnSafeModeStatusChanged(
@NonNull ParcelUuid subGroup,
@NonNull String pkgName,
int uid,
@@ -885,12 +952,13 @@
mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
- triggerVcnSafeMode(subGroup, snapshot);
+ triggerVcnSafeMode(subGroup, snapshot, true /* enterSafeMode */);
}
@Test
- public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception {
- triggerVcnStatusCallbackOnEnteredSafeMode(
+ public void testVcnStatusCallbackOnSafeModeStatusChangedWithCarrierPrivileges()
+ throws Exception {
+ triggerVcnStatusCallbackOnSafeModeStatusChanged(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
@@ -901,8 +969,9 @@
}
@Test
- public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception {
- triggerVcnStatusCallbackOnEnteredSafeMode(
+ public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutCarrierPrivileges()
+ throws Exception {
+ triggerVcnStatusCallbackOnSafeModeStatusChanged(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
@@ -914,8 +983,9 @@
}
@Test
- public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception {
- triggerVcnStatusCallbackOnEnteredSafeMode(
+ public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutLocationPermission()
+ throws Exception {
+ triggerVcnStatusCallbackOnSafeModeStatusChanged(
TEST_UUID_1,
TEST_PACKAGE_NAME,
TEST_UID,
diff --git a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
index ed2e4d9..b592000 100644
--- a/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/UnderlyingNetworkTrackerTest.java
@@ -158,7 +158,7 @@
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
- eq(getCellRequestForSubId(subId, expectedSubIds)),
+ eq(getCellRequestForSubId(subId)),
any(),
any(NetworkBringupCallback.class));
}
@@ -189,30 +189,30 @@
}
private NetworkRequest getWifiRequest(Set<Integer> netCapsSubIds) {
- return getExpectedRequestBase(netCapsSubIds)
+ return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .setSubIds(netCapsSubIds)
.build();
}
- private NetworkRequest getCellRequestForSubId(int subId, Set<Integer> netCapsSubIds) {
- return getExpectedRequestBase(netCapsSubIds)
+ private NetworkRequest getCellRequestForSubId(int subId) {
+ return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
}
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
- return getExpectedRequestBase(netCapsSubIds).build();
+ return getExpectedRequestBase().setSubIds(netCapsSubIds).build();
}
- private NetworkRequest.Builder getExpectedRequestBase(Set<Integer> subIds) {
+ private NetworkRequest.Builder getExpectedRequestBase() {
final NetworkRequest.Builder builder =
new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
- .setSubIds(subIds);
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
return builder;
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index ca6448c..34c0018 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -40,6 +41,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.net.ConnectivityManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
@@ -58,19 +61,29 @@
import org.mockito.ArgumentCaptor;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
/** Tests for VcnGatewayConnection.ConnectedState */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
private VcnIkeSession mIkeSession;
+ private NetworkAgent mNetworkAgent;
@Before
public void setUp() throws Exception {
super.setUp();
+ mNetworkAgent = mock(NetworkAgent.class);
+ doReturn(mNetworkAgent)
+ .when(mDeps)
+ .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any());
+
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
@@ -159,21 +172,44 @@
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
}
- @Test
- public void testChildOpenedRegistersNetwork() throws Exception {
- // Verify scheduled but not canceled when entering ConnectedState
- verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ private void triggerChildOpened() {
+ triggerChildOpened(Collections.singletonList(TEST_INTERNAL_ADDR), TEST_DNS_ADDR);
+ }
+ private void triggerChildOpened(List<LinkAddress> internalAddresses, InetAddress dnsAddress) {
final VcnChildSessionConfiguration mMockChildSessionConfig =
mock(VcnChildSessionConfiguration.class);
- doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
- .when(mMockChildSessionConfig)
- .getInternalAddresses();
- doReturn(Collections.singletonList(TEST_DNS_ADDR))
+ doReturn(internalAddresses).when(mMockChildSessionConfig).getInternalAddresses();
+ doReturn(Collections.singletonList(dnsAddress))
.when(mMockChildSessionConfig)
.getInternalDnsServers();
getChildSessionCallback().onOpened(mMockChildSessionConfig);
+ }
+
+ private void triggerValidation(int status) {
+ final ArgumentCaptor<Consumer<Integer>> validationCallbackCaptor =
+ ArgumentCaptor.forClass(Consumer.class);
+ verify(mDeps)
+ .newNetworkAgent(
+ any(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ any(),
+ any(),
+ any(),
+ validationCallbackCaptor.capture());
+
+ validationCallbackCaptor.getValue().accept(status);
+ }
+
+ @Test
+ public void testChildOpenedRegistersNetwork() throws Exception {
+ // Verify scheduled but not canceled when entering ConnectedState
+ verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+ triggerChildOpened();
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
@@ -182,15 +218,20 @@
ArgumentCaptor.forClass(LinkProperties.class);
final ArgumentCaptor<NetworkCapabilities> ncCaptor =
ArgumentCaptor.forClass(NetworkCapabilities.class);
- verify(mConnMgr)
- .registerNetworkAgent(
- any(),
- any(),
- lpCaptor.capture(),
+ verify(mDeps)
+ .newNetworkAgent(
+ eq(mVcnContext),
+ any(String.class),
ncCaptor.capture(),
+ lpCaptor.capture(),
+ anyInt(),
+ argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE),
any(),
any(),
- anyInt());
+ any());
+ verify(mNetworkAgent).register();
+ verify(mNetworkAgent).markConnected();
+
verify(mIpSecSvc)
.addAddressToTunnelInterface(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
@@ -208,9 +249,78 @@
// Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
// canceled
- mGatewayConnection.mNetworkAgent.onValidationStatus(
- NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */);
+ triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
verify(mSafeModeTimeoutAlarm).cancel();
+ assertFalse(mGatewayConnection.isInSafeMode());
+ }
+
+ @Test
+ public void testInternalAndDnsAddressesChanged() throws Exception {
+ final List<LinkAddress> startingInternalAddrs =
+ Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR, TEST_INTERNAL_ADDR_2});
+ triggerChildOpened(startingInternalAddrs, TEST_DNS_ADDR);
+ mTestLooper.dispatchAll();
+
+ for (LinkAddress addr : startingInternalAddrs) {
+ verify(mIpSecSvc)
+ .addAddressToTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any());
+ }
+
+ verify(mDeps)
+ .newNetworkAgent(
+ any(),
+ any(),
+ any(),
+ argThat(
+ lp ->
+ startingInternalAddrs.equals(lp.getLinkAddresses())
+ && Collections.singletonList(TEST_DNS_ADDR)
+ .equals(lp.getDnsServers())),
+ anyInt(),
+ any(),
+ any(),
+ any(),
+ any());
+
+ // Trigger another connection event, and verify that the addresses change
+ final List<LinkAddress> newInternalAddrs =
+ Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR_2, TEST_INTERNAL_ADDR_3});
+ triggerChildOpened(newInternalAddrs, TEST_DNS_ADDR_2);
+ mTestLooper.dispatchAll();
+
+ // Verify addresses on tunnel network added/removed
+ for (LinkAddress addr : newInternalAddrs) {
+ verify(mIpSecSvc)
+ .addAddressToTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any());
+ }
+ verify(mIpSecSvc)
+ .removeAddressFromTunnelInterface(
+ eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+ // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties
+ // is mockable
+
+ // Verify that IpSecTunnelInterface only created once
+ verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+ verifyNoMoreInteractions(mIpSecSvc);
+ }
+
+ @Test
+ public void testSuccessfulConnectionExitsSafeMode() throws Exception {
+ verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+ mGatewayConnection.mConnectedState);
+
+ assertTrue(mGatewayConnection.isInSafeMode());
+ assertFalse(mGatewayConnection.isQuitting());
+
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+
+ assertFalse(mGatewayConnection.isInSafeMode());
}
@Test
@@ -241,7 +351,7 @@
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
- eq(mConfig.getExposedCapabilities()),
+ eq(mConfig.getGatewayConnectionName()),
eq(VCN_ERROR_CODE_INTERNAL_ERROR),
any(),
any());
@@ -275,7 +385,10 @@
verify(mGatewayStatusCallback)
.onGatewayConnectionError(
- eq(mConfig.getExposedCapabilities()), eq(expectedErrorType), any(), any());
+ eq(mConfig.getGatewayConnectionName()),
+ eq(expectedErrorType),
+ any(),
+ any());
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index 7afa449..bfe8c73 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -118,8 +118,9 @@
}
@Test
- public void testSafeModeTimeoutNotifiesCallback() {
- verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
+ public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+ verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+ mGatewayConnection.mConnectingState);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 99feffd..9da8b45 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -86,8 +86,9 @@
}
@Test
- public void testSafeModeTimeoutNotifiesCallback() {
- verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
+ public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+ verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+ mGatewayConnection.mDisconnectingState);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 85a0277..6dbf7d5 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -96,8 +96,9 @@
}
@Test
- public void testSafeModeTimeoutNotifiesCallback() {
- verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
+ public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+ verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+ mGatewayConnection.mRetryTimeoutState);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a660735..c5ed8f6 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -21,6 +21,8 @@
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -42,6 +44,7 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
@@ -71,8 +74,14 @@
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
protected static final InetAddress TEST_DNS_ADDR =
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+ protected static final InetAddress TEST_DNS_ADDR_2 =
+ InetAddresses.parseNumericAddress("2001:DB8:0:2::");
protected static final LinkAddress TEST_INTERNAL_ADDR =
- new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:1::"), 64);
+ protected static final LinkAddress TEST_INTERNAL_ADDR_2 =
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:2::"), 64);
+ protected static final LinkAddress TEST_INTERNAL_ADDR_3 =
+ new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:3::"), 64);
protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
@@ -267,7 +276,12 @@
expectCanceled);
}
- protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) {
+ protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+ @NonNull State expectedState) {
+ // Set a NetworkAgent, and expect it to be unregistered and cleared
+ final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class);
+ mGatewayConnection.setNetworkAgent(mockNetworkAgent);
+
// SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
// state)
final Runnable delayedEvent =
@@ -275,7 +289,11 @@
delayedEvent.run();
mTestLooper.dispatchAll();
- verify(mGatewayStatusCallback).onEnteredSafeMode();
+ verify(mGatewayStatusCallback).onSafeModeStatusChanged();
assertEquals(expectedState, mGatewayConnection.getCurrentState());
+ assertTrue(mGatewayConnection.isInSafeMode());
+
+ verify(mockNetworkAgent).unregister();
+ assertNull(mGatewayConnection.getNetworkAgent());
}
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index c853fc5..90eb75e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -19,12 +19,13 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
+import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -51,7 +52,8 @@
import org.junit.Test;
import org.mockito.ArgumentCaptor;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -132,6 +134,7 @@
private void startVcnGatewayWithCapabilities(
NetworkRequestListener requestListener, int... netCapabilities) {
final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+ requestBuilder.addTransportType(TRANSPORT_CELLULAR);
for (final int netCapability : netCapabilities) {
requestBuilder.addCapability(netCapability);
}
@@ -140,7 +143,7 @@
mTestLooper.dispatchAll();
}
- private void verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(boolean isActive) {
+ private void verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(int status) {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
startVcnGatewayWithCapabilities(requestListener, TEST_CAPS[0]);
@@ -150,25 +153,24 @@
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
- mVcn.setIsActive(isActive);
+ mVcn.setStatus(status);
mVcn.updateSubscriptionSnapshot(updatedSnapshot);
mTestLooper.dispatchAll();
for (final VcnGatewayConnection gateway : gatewayConnections) {
- verify(gateway, isActive ? times(1) : never())
- .updateSubscriptionSnapshot(eq(updatedSnapshot));
+ verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
- verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(true /* isActive */);
+ verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_ACTIVE);
}
@Test
- public void testSubscriptionSnapshotUpdatesVcnGatewayConnectionsWhileInactive() {
- verifyUpdateSubscriptionSnapshotNotifiesConnectionGateways(false /* isActive */);
+ public void testSubscriptionSnapshotUpdatesVcnGatewayConnectionsInSafeMode() {
+ verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_SAFE_MODE);
}
private void triggerVcnRequestListeners(NetworkRequestListener requestListener) {
@@ -198,32 +200,53 @@
private void verifySafeMode(
NetworkRequestListener requestListener,
- Set<VcnGatewayConnection> expectedGatewaysTornDown) {
- assertFalse(mVcn.isActive());
- for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
- verify(gatewayConnection).teardownAsynchronously();
+ Set<VcnGatewayConnection> activeGateways,
+ boolean expectInSafeMode) {
+ for (VcnGatewayConnection gatewayConnection : activeGateways) {
+ verify(gatewayConnection, never()).teardownAsynchronously();
}
- verify(mVcnNetworkProvider).unregisterListener(requestListener);
- verify(mVcnCallback).onEnteredSafeMode();
+
+ assertEquals(
+ expectInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE,
+ mVcn.getStatus());
+ verify(mVcnCallback).onSafeModeStatusChanged(expectInSafeMode);
}
@Test
- public void testGatewayEnteringSafeModeNotifiesVcn() {
+ public void testGatewayEnteringAndExitingSafeModeNotifiesVcn() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
final Set<VcnGatewayConnection> gatewayConnections =
startGatewaysAndGetGatewayConnections(requestListener);
- // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
- // all Gateways
+ // Doesn't matter which callback this gets, or which VCN is in safe mode - any Gateway
+ // entering Safemode should trigger safe mode
final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
- statusCallback.onEnteredSafeMode();
+ final VcnGatewayConnection gatewayConnection = gatewayConnections.iterator().next();
+
+ doReturn(true).when(gatewayConnection).isInSafeMode();
+ statusCallback.onSafeModeStatusChanged();
mTestLooper.dispatchAll();
- verifySafeMode(requestListener, gatewayConnections);
+ verifySafeMode(requestListener, gatewayConnections, true /* expectInSafeMode */);
+
+ // Verify that when all GatewayConnections exit safe mode, the VCN also exits safe mode
+ doReturn(false).when(gatewayConnection).isInSafeMode();
+ statusCallback.onSafeModeStatusChanged();
+ mTestLooper.dispatchAll();
+
+ verifySafeMode(requestListener, gatewayConnections, false /* expectInSafeMode */);
+
+ // Re-trigger, verify safe mode callback does not get fired again for identical state
+ statusCallback.onSafeModeStatusChanged();
+ mTestLooper.dispatchAll();
+
+ // Expect only once still; from above.
+ verify(mVcnCallback).onSafeModeStatusChanged(false);
}
- @Test
- public void testGatewayQuit() {
+ private void verifyGatewayQuit(int status) {
+ mVcn.setStatus(status);
+
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
final Set<VcnGatewayConnection> gatewayConnections =
new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
@@ -236,7 +259,7 @@
assertEquals(1, mVcn.getVcnGatewayConnections().size());
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
- // Verify that the VcnGatewayConnection is restarted
+ // Verify that the VcnGatewayConnection is restarted if a request exists for it
triggerVcnRequestListeners(requestListener);
mTestLooper.dispatchAll();
assertEquals(2, mVcn.getVcnGatewayConnections().size());
@@ -250,21 +273,13 @@
}
@Test
- public void testGatewayQuitWhileInactive() {
- final NetworkRequestListener requestListener = verifyAndGetRequestListener();
- final Set<VcnGatewayConnection> gatewayConnections =
- new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
+ public void testGatewayQuitReevaluatesRequests() {
+ verifyGatewayQuit(VCN_STATUS_CODE_ACTIVE);
+ }
- mVcn.teardownAsynchronously();
- mTestLooper.dispatchAll();
-
- final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
- statusCallback.onQuit();
- mTestLooper.dispatchAll();
-
- // Verify that the VCN requests the networkRequests be resent
- assertEquals(1, mVcn.getVcnGatewayConnections().size());
- verify(mVcnNetworkProvider, never()).resendAllRequests(requestListener);
+ @Test
+ public void testGatewayQuitReevaluatesRequestsInSafeMode() {
+ verifyGatewayQuit(VCN_STATUS_CODE_SAFE_MODE);
}
@Test
@@ -274,11 +289,12 @@
assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size());
// Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn
- // down
- final VcnGatewayConnectionConfig activeConfig =
- VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[0]);
- final VcnGatewayConnectionConfig removedConfig =
- VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps(TEST_CAPS[1]);
+ // down. Reuse existing VcnGatewayConnectionConfig so that the gateway connection name
+ // matches.
+ final List<VcnGatewayConnectionConfig> currentConfigs =
+ new ArrayList<>(mVcn.getVcnGatewayConnectionConfigMap().keySet());
+ final VcnGatewayConnectionConfig activeConfig = currentConfigs.get(0);
+ final VcnGatewayConnectionConfig removedConfig = currentConfigs.get(1);
final VcnConfig updatedConfig =
new VcnConfig.Builder(mContext).addGatewayConnectionConfig(activeConfig).build();
@@ -293,49 +309,4 @@
verify(removedGatewayConnection).teardownAsynchronously();
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
-
- @Test
- public void testUpdateConfigExitsSafeMode() {
- final NetworkRequestListener requestListener = verifyAndGetRequestListener();
- final Set<VcnGatewayConnection> gatewayConnections =
- new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
-
- final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
- statusCallback.onEnteredSafeMode();
- mTestLooper.dispatchAll();
- verifySafeMode(requestListener, gatewayConnections);
-
- doAnswer(invocation -> {
- final NetworkRequestListener listener = invocation.getArgument(0);
- triggerVcnRequestListeners(listener);
- return null;
- }).when(mVcnNetworkProvider).registerListener(eq(requestListener));
-
- mVcn.updateConfig(mConfig);
- mTestLooper.dispatchAll();
-
- // Registered on start, then re-registered with new configs
- verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
- assertTrue(mVcn.isActive());
- for (final int[] caps : TEST_CAPS) {
- // Expect each gateway connection created only on initial startup
- verify(mDeps)
- .newVcnGatewayConnection(
- eq(mVcnContext),
- eq(TEST_SUB_GROUP),
- eq(mSubscriptionSnapshot),
- argThat(config -> Arrays.equals(caps, config.getExposedCapabilities())),
- any());
- }
- }
-
- @Test
- public void testIgnoreNetworkRequestWhileInactive() {
- mVcn.setIsActive(false /* isActive */);
-
- final NetworkRequestListener requestListener = verifyAndGetRequestListener();
- triggerVcnRequestListeners(requestListener);
-
- verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any());
- }
}
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 18c4054..73eacc0 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -35,7 +35,7 @@
PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
RE=$(echo ${PACKAGES} | sed "s/ /|/g")
git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
- ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file))
+ ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file))
if [[ -n "${ENTRIES}" ]]; then
echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m"
echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m"