summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2021-01-06 00:52:32 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2021-01-06 00:52:32 +0000
commit3443d2b671bb7d8e9bd48da1ee40286bb71d73f0 (patch)
tree0036aa01e1637e73d918d4f5bfc5f65f6971a8d8 /core/java
parent170d46a5e561dc84ce673454ff8c2d5171af3ce5 (diff)
parent30841f177c920b81d163a1c59e33c3e0e319532d (diff)
Merge "Keystore 2.0: Android Protected Confirmation" am: 30841f177c
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1508897 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: If0d51926f5045c83c12e04ba216f00e7cb287745
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/security/ConfirmationPrompt.java193
1 files changed, 157 insertions, 36 deletions
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index f67af85d00e3..232903724d82 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -21,6 +21,7 @@ import android.content.ContentResolver;
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;
@@ -36,15 +37,15 @@ import java.util.concurrent.Executor;
* compromised. Implementing confirmation prompts with these guarantees requires dedicated
* hardware-support and may not always be available.
*
- * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> -
+ * <p>Confirmation prompts are typically used with an external entity - the <i>Relying Party</i> -
* in the following way. The setup steps are as follows:
* <ul>
* <li> Before first use, the application generates a key-pair with the
* {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
- * CONFIRMATION tag} set. Device attestation,
- * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to
- * generate a certificate chain that includes the public key (<code>Kpub</code> in the following)
- * of the newly generated key.
+ * CONFIRMATION tag} set. AndroidKeyStore key attestation, e.g.,
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}
+ * is used to generate a certificate chain that includes the public key (<code>Kpub</code> in the
+ * following) of the newly generated key.
* <li> The application sends <code>Kpub</code> and the certificate chain resulting from device
* attestation to the <i>Relying Party</i>.
* <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root
@@ -78,9 +79,10 @@ import java.util.concurrent.Executor;
* previously created nonce. If all checks passes, the transaction is executed.
* </ul>
*
- * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the
- * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
- * along the nonce in the <code>extraData</code> blob.
+ * <p>Note: It is vital to check the <code>promptText</code> because this is the only part that
+ * the user has approved. To avoid writing parsers for all of the possible locales, it is
+ * recommended that the <i>Relying Party</i> uses the same string generator as used on the device
+ * and performs a simple string comparison.
*/
public class ConfirmationPrompt {
private static final String TAG = "ConfirmationPrompt";
@@ -92,6 +94,14 @@ public class ConfirmationPrompt {
private Context mContext;
private final KeyStore mKeyStore = KeyStore.getInstance();
+ private AndroidProtectedConfirmation mProtectedConfirmation;
+
+ private AndroidProtectedConfirmation getService() {
+ if (mProtectedConfirmation == null) {
+ mProtectedConfirmation = new AndroidProtectedConfirmation();
+ }
+ return mProtectedConfirmation;
+ }
private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
ConfirmationCallback callback) {
@@ -119,6 +129,32 @@ public class ConfirmationPrompt {
}
}
+ private void doCallback2(int responseCode, byte[] dataThatWasConfirmed,
+ ConfirmationCallback callback) {
+ switch (responseCode) {
+ case AndroidProtectedConfirmation.ERROR_OK:
+ callback.onConfirmed(dataThatWasConfirmed);
+ break;
+
+ case AndroidProtectedConfirmation.ERROR_CANCELED:
+ callback.onDismissed();
+ break;
+
+ case AndroidProtectedConfirmation.ERROR_ABORTED:
+ callback.onCanceled();
+ break;
+
+ case AndroidProtectedConfirmation.ERROR_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 final android.os.IBinder mCallbackBinder =
new android.security.IConfirmationPromptCallback.Stub() {
@Override
@@ -144,6 +180,29 @@ public class ConfirmationPrompt {
}
};
+ private final android.security.apc.IConfirmationCallback mConfirmationCallback =
+ new android.security.apc.IConfirmationCallback.Stub() {
+ @Override
+ public void onCompleted(int result, byte[] dataThatWasConfirmed)
+ throws android.os.RemoteException {
+ if (mCallback != null) {
+ ConfirmationCallback callback = mCallback;
+ Executor executor = mExecutor;
+ mCallback = null;
+ mExecutor = null;
+ if (executor == null) {
+ doCallback2(result, dataThatWasConfirmed, callback);
+ } else {
+ executor.execute(new Runnable() {
+ @Override public void run() {
+ doCallback2(result, dataThatWasConfirmed, callback);
+ }
+ });
+ }
+ }
+ }
+ };
+
/**
* A builder that collects arguments, to be shown on the system-provided confirmation prompt.
*/
@@ -211,6 +270,9 @@ public class ConfirmationPrompt {
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,
@@ -226,6 +288,22 @@ public class ConfirmationPrompt {
return uiOptionsAsFlags;
}
+ private int 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 |= AndroidProtectedConfirmation.FLAG_UI_OPTION_INVERTED;
+ }
+ float fontScale = Settings.System.getFloat(contentResolver,
+ Settings.System.FONT_SCALE, (float) 1.0);
+ if (fontScale > 1.0) {
+ uiOptionsAsFlags |= AndroidProtectedConfirmation.FLAG_UI_OPTION_MAGNIFIED;
+ }
+ return uiOptionsAsFlags;
+ }
+
private static boolean isAccessibilityServiceRunning(Context context) {
boolean serviceRunning = false;
try {
@@ -270,29 +348,53 @@ public class ConfirmationPrompt {
mCallback = callback;
mExecutor = executor;
- int uiOptionsAsFlags = getUiOptionsAsFlags();
String locale = Locale.getDefault().toLanguageTag();
- int responseCode = mKeyStore.presentConfirmationPrompt(
- mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
- switch (responseCode) {
- case KeyStore.CONFIRMATIONUI_OK:
- return;
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ int uiOptionsAsFlags = getUiOptionsAsFlags2();
+ 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_UNIMPLEMENTED:
+ throw new ConfirmationNotAvailableException();
- case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
- throw new ConfirmationAlreadyPresentingException();
+ 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_UNIMPLEMENTED:
- throw new ConfirmationNotAvailableException();
+ case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
+ throw new ConfirmationAlreadyPresentingException();
- case KeyStore.CONFIRMATIONUI_UIERROR:
- throw new IllegalArgumentException();
+ case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
+ throw new ConfirmationNotAvailableException();
- default:
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from presentConfirmationPrompt() call.");
- throw new IllegalArgumentException();
+ case KeyStore.CONFIRMATIONUI_UIERROR:
+ throw new IllegalArgumentException();
+
+ default:
+ // Unexpected error code.
+ Log.w(TAG,
+ "Unexpected responseCode=" + responseCode
+ + " from presentConfirmationPrompt() call.");
+ throw new IllegalArgumentException();
+ }
}
}
@@ -306,17 +408,33 @@ public class ConfirmationPrompt {
* @throws IllegalStateException if no prompt is currently being presented.
*/
public void cancelPrompt() {
- int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
- if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
- return;
- } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
- throw new IllegalStateException();
+ 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();
+ }
} else {
- // Unexpected error code.
- Log.w(TAG,
- "Unexpected responseCode=" + responseCode
- + " from cancelConfirmationPrompt() call.");
- throw new IllegalStateException();
+ 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();
+ }
}
}
@@ -330,6 +448,9 @@ public class ConfirmationPrompt {
if (isAccessibilityServiceRunning(context)) {
return false;
}
+ if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ return new AndroidProtectedConfirmation().isConfirmationPromptSupported();
+ }
return KeyStore.getInstance().isConfirmationPromptSupported();
}
}