Loading api/current.txt +36 −0 Original line number Diff line number Diff line Loading @@ -38198,6 +38198,37 @@ package android.security { method public java.security.KeyPair getKeyPair(); } public class ConfirmationAlreadyPresentingException extends java.lang.Exception { ctor public ConfirmationAlreadyPresentingException(); ctor public ConfirmationAlreadyPresentingException(java.lang.String); } public abstract class ConfirmationCallback { ctor public ConfirmationCallback(); method public void onConfirmedByUser(byte[]); method public void onDismissedByApplication(); method public void onDismissedByUser(); method public void onError(java.lang.Exception); } public class ConfirmationDialog { method public void cancelPrompt(); method public static boolean isSupported(); method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException; } public static class ConfirmationDialog.Builder { ctor public ConfirmationDialog.Builder(); method public android.security.ConfirmationDialog build(android.content.Context); method public android.security.ConfirmationDialog.Builder setExtraData(byte[]); method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence); } public class ConfirmationNotAvailableException extends java.lang.Exception { ctor public ConfirmationNotAvailableException(); ctor public ConfirmationNotAvailableException(java.lang.String); } public final class KeyChain { ctor public KeyChain(); method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String); Loading Loading @@ -38307,6 +38338,7 @@ package android.security.keystore { method public boolean isTrustedUserPresenceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public static final class KeyGenParameterSpec.Builder { Loading Loading @@ -38334,6 +38366,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); method public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean); } public class KeyInfo implements java.security.spec.KeySpec { Loading @@ -38355,6 +38388,7 @@ package android.security.keystore { method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public class KeyNotYetValidException extends java.security.InvalidKeyException { Loading Loading @@ -38422,6 +38456,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public static final class KeyProtection.Builder { Loading @@ -38440,6 +38475,7 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean); } public class StrongBoxUnavailableException extends java.security.ProviderException { core/java/android/security/ConfirmationAlreadyPresentingException.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright 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; /** * This exception is thrown when presenting a prompt fails because another prompt is already * being presented. */ public class ConfirmationAlreadyPresentingException extends Exception { public ConfirmationAlreadyPresentingException() {} public ConfirmationAlreadyPresentingException(String message) { super(message); } } core/java/android/security/ConfirmationCallback.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright 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; import android.annotation.NonNull; /** * Callback class used when signaling that a prompt is no longer being presented. */ public abstract class ConfirmationCallback { /** * Called when the requested prompt was accepted by the user. * * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a> * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as * CBOR byte string (type 2). Other keys may be added in the future. * * @param dataThatWasConfirmed the data that was confirmed, see above for the format. */ public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {} /** * Called when the requested prompt was dismissed (not accepted) by the user. */ public void onDismissedByUser() {} /** * Called when the requested prompt was dismissed by the application. */ public void onDismissedByApplication() {} /** * Called when the requested prompt was dismissed because of a low-level error. * * @param e an exception representing the error. */ public void onError(Exception e) {} } core/java/android/security/ConfirmationDialog.java 0 → 100644 +283 −0 Original line number Diff line number Diff line /* * Copyright 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; import android.annotation.NonNull; import android.content.Context; import android.text.TextUtils; import android.util.Log; import java.util.Locale; import java.util.concurrent.Executor; /** * Class used for displaying confirmation prompts. * * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are * implemented in a way that a positive response indicates with high confidence that the user has * seen the given text, even if the Android framework (including the kernel) was * 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> - * 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. * <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 * certificate is what is expected (e.g. a certificate from Google), each certificate signs the * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate * asserts that <code>Kpub</code> has the * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired * CONFIRMATION tag} set. * Additionally the relying party stores <code>Kpub</code> and associates it with the device * it was received from. * </ul> * * <p>The <i>Relying Party</i> is typically an external device (for example connected via * Bluetooth) or application server. * * <p>Before executing a transaction which requires a high assurance of user content, the * application does the following: * <ul> * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as * the <code>extraData</code> (via the Builder helper class) to the * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally * since it'll use it in a later step. * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the * user, the <code>extraData</code> parameter, and possibly other data. * <li> The application signs the <i>Confirmation Response</i> with the previously created key and * sends the blob and the signature to the <i>Relying Party</i>. * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the * 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. */ public class ConfirmationDialog { private static final String TAG = "ConfirmationDialog"; private CharSequence mPromptText; private byte[] mExtraData; private ConfirmationCallback mCallback; private Executor mExecutor; private final KeyStore mKeyStore = KeyStore.getInstance(); private void doCallback(int responseCode, byte[] dataThatWasConfirmed, ConfirmationCallback callback) { switch (responseCode) { case KeyStore.CONFIRMATIONUI_OK: callback.onConfirmedByUser(dataThatWasConfirmed); break; case KeyStore.CONFIRMATIONUI_CANCELED: callback.onDismissedByUser(); break; case KeyStore.CONFIRMATIONUI_ABORTED: callback.onDismissedByApplication(); 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 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); } }); } } } }; /** * A builder that collects arguments, to be shown on the system-provided confirmation dialog. */ public static class Builder { private CharSequence mPromptText; private byte[] mExtraData; /** * Creates a builder for the confirmation dialog. */ public Builder() { } /** * Sets the prompt text for the dialog. * * @param promptText the text to present in the prompt. * @return the builder. */ public Builder setPromptText(CharSequence promptText) { mPromptText = promptText; return this; } /** * Sets the extra data for the dialog. * * @param extraData data to include in the response data. * @return the builder. */ public Builder setExtraData(byte[] extraData) { mExtraData = extraData; return this; } /** * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder. * * @param context the application context * @return a {@link ConfirmationDialog} * @throws IllegalArgumentException if any of the required fields are not set. */ public ConfirmationDialog build(Context context) { if (TextUtils.isEmpty(mPromptText)) { throw new IllegalArgumentException("prompt text must be set and non-empty"); } if (mExtraData == null) { throw new IllegalArgumentException("extraData must be set"); } return new ConfirmationDialog(mPromptText, mExtraData); } } private ConfirmationDialog(CharSequence promptText, byte[] extraData) { mPromptText = promptText; mExtraData = extraData; } /** * Requests a confirmation prompt to be presented to the user. * * When the prompt is no longer being presented, one of the methods in * {@link ConfirmationCallback} is called on the supplied callback object. * * @param executor the executor identifying the thread that will receive the callback. * @param callback the callback to use when the dialog is done showing. * @throws IllegalArgumentException if the prompt text is too long or malfomed. * @throws ConfirmationAlreadyPresentingException if another prompt is being presented. * @throws ConfirmationNotAvailableException if confirmation prompts are not supported. */ public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback) throws ConfirmationAlreadyPresentingException, ConfirmationNotAvailableException { if (mCallback != null) { throw new ConfirmationAlreadyPresentingException(); } mCallback = callback; mExecutor = executor; int uiOptionsAsFlags = 0; // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed. String locale = Locale.getDefault().toLanguageTag(); 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(); } } /** * Cancels a prompt currently being displayed. * * On success, the * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on * the supplied callback object will be called asynchronously. * * @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(); } else { // Unexpected error code. Log.w(TAG, "Unexpected responseCode=" + responseCode + " from cancelConfirmationPrompt() call."); throw new IllegalStateException(); } } /** * Checks if the device supports confirmation prompts. * * @return true if confirmation prompts are supported by the device. */ public static boolean isSupported() { // TODO: read and return system property. return true; } } core/java/android/security/ConfirmationNotAvailableException.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright 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; /** * This exception is thrown when presenting a prompt fails because the the environment lacks * facilities for showing confirmations. */ public class ConfirmationNotAvailableException extends Exception { public ConfirmationNotAvailableException() { } public ConfirmationNotAvailableException(String message) { super(message); } } Loading
api/current.txt +36 −0 Original line number Diff line number Diff line Loading @@ -38198,6 +38198,37 @@ package android.security { method public java.security.KeyPair getKeyPair(); } public class ConfirmationAlreadyPresentingException extends java.lang.Exception { ctor public ConfirmationAlreadyPresentingException(); ctor public ConfirmationAlreadyPresentingException(java.lang.String); } public abstract class ConfirmationCallback { ctor public ConfirmationCallback(); method public void onConfirmedByUser(byte[]); method public void onDismissedByApplication(); method public void onDismissedByUser(); method public void onError(java.lang.Exception); } public class ConfirmationDialog { method public void cancelPrompt(); method public static boolean isSupported(); method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException; } public static class ConfirmationDialog.Builder { ctor public ConfirmationDialog.Builder(); method public android.security.ConfirmationDialog build(android.content.Context); method public android.security.ConfirmationDialog.Builder setExtraData(byte[]); method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence); } public class ConfirmationNotAvailableException extends java.lang.Exception { ctor public ConfirmationNotAvailableException(); ctor public ConfirmationNotAvailableException(java.lang.String); } public final class KeyChain { ctor public KeyChain(); method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String); Loading Loading @@ -38307,6 +38338,7 @@ package android.security.keystore { method public boolean isTrustedUserPresenceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public static final class KeyGenParameterSpec.Builder { Loading Loading @@ -38334,6 +38366,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); method public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean); } public class KeyInfo implements java.security.spec.KeySpec { Loading @@ -38355,6 +38388,7 @@ package android.security.keystore { method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public class KeyNotYetValidException extends java.security.InvalidKeyException { Loading Loading @@ -38422,6 +38456,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); } public static final class KeyProtection.Builder { Loading @@ -38440,6 +38475,7 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); method public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean); } public class StrongBoxUnavailableException extends java.security.ProviderException {
core/java/android/security/ConfirmationAlreadyPresentingException.java 0 → 100644 +29 −0 Original line number Diff line number Diff line /* * Copyright 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; /** * This exception is thrown when presenting a prompt fails because another prompt is already * being presented. */ public class ConfirmationAlreadyPresentingException extends Exception { public ConfirmationAlreadyPresentingException() {} public ConfirmationAlreadyPresentingException(String message) { super(message); } }
core/java/android/security/ConfirmationCallback.java 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright 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; import android.annotation.NonNull; /** * Callback class used when signaling that a prompt is no longer being presented. */ public abstract class ConfirmationCallback { /** * Called when the requested prompt was accepted by the user. * * The format of 'dataThatWasConfirmed' parameter is a <a href="http://cbor.io/">CBOR</a> * encoded map (type 5) with (at least) the keys <strong>prompt</strong> and * <strong>extra</strong>. The keys are encoded as CBOR text string (type 3). The value of * promptText is encoded as CBOR text string (type 3), and the value of extraData is encoded as * CBOR byte string (type 2). Other keys may be added in the future. * * @param dataThatWasConfirmed the data that was confirmed, see above for the format. */ public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {} /** * Called when the requested prompt was dismissed (not accepted) by the user. */ public void onDismissedByUser() {} /** * Called when the requested prompt was dismissed by the application. */ public void onDismissedByApplication() {} /** * Called when the requested prompt was dismissed because of a low-level error. * * @param e an exception representing the error. */ public void onError(Exception e) {} }
core/java/android/security/ConfirmationDialog.java 0 → 100644 +283 −0 Original line number Diff line number Diff line /* * Copyright 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; import android.annotation.NonNull; import android.content.Context; import android.text.TextUtils; import android.util.Log; import java.util.Locale; import java.util.concurrent.Executor; /** * Class used for displaying confirmation prompts. * * <p>Confirmation prompts are prompts shown to the user to confirm a given text and are * implemented in a way that a positive response indicates with high confidence that the user has * seen the given text, even if the Android framework (including the kernel) was * 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> - * 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. * <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 * certificate is what is expected (e.g. a certificate from Google), each certificate signs the * next one in the chain, ending with <code>Kpub</code>, and that the attestation certificate * asserts that <code>Kpub</code> has the * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired * CONFIRMATION tag} set. * Additionally the relying party stores <code>Kpub</code> and associates it with the device * it was received from. * </ul> * * <p>The <i>Relying Party</i> is typically an external device (for example connected via * Bluetooth) or application server. * * <p>Before executing a transaction which requires a high assurance of user content, the * application does the following: * <ul> * <li> The application gets a cryptographic nonce from the <i>Relying Party</i> and passes this as * the <code>extraData</code> (via the Builder helper class) to the * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally * since it'll use it in a later step. * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the * user, the <code>extraData</code> parameter, and possibly other data. * <li> The application signs the <i>Confirmation Response</i> with the previously created key and * sends the blob and the signature to the <i>Relying Party</i>. * <li> The <i>Relying Party</i> checks that the signature was made with <code>Kpub</code> and then * extracts <code>promptText</code> matches what is expected and <code>extraData</code> matches the * 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. */ public class ConfirmationDialog { private static final String TAG = "ConfirmationDialog"; private CharSequence mPromptText; private byte[] mExtraData; private ConfirmationCallback mCallback; private Executor mExecutor; private final KeyStore mKeyStore = KeyStore.getInstance(); private void doCallback(int responseCode, byte[] dataThatWasConfirmed, ConfirmationCallback callback) { switch (responseCode) { case KeyStore.CONFIRMATIONUI_OK: callback.onConfirmedByUser(dataThatWasConfirmed); break; case KeyStore.CONFIRMATIONUI_CANCELED: callback.onDismissedByUser(); break; case KeyStore.CONFIRMATIONUI_ABORTED: callback.onDismissedByApplication(); 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 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); } }); } } } }; /** * A builder that collects arguments, to be shown on the system-provided confirmation dialog. */ public static class Builder { private CharSequence mPromptText; private byte[] mExtraData; /** * Creates a builder for the confirmation dialog. */ public Builder() { } /** * Sets the prompt text for the dialog. * * @param promptText the text to present in the prompt. * @return the builder. */ public Builder setPromptText(CharSequence promptText) { mPromptText = promptText; return this; } /** * Sets the extra data for the dialog. * * @param extraData data to include in the response data. * @return the builder. */ public Builder setExtraData(byte[] extraData) { mExtraData = extraData; return this; } /** * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder. * * @param context the application context * @return a {@link ConfirmationDialog} * @throws IllegalArgumentException if any of the required fields are not set. */ public ConfirmationDialog build(Context context) { if (TextUtils.isEmpty(mPromptText)) { throw new IllegalArgumentException("prompt text must be set and non-empty"); } if (mExtraData == null) { throw new IllegalArgumentException("extraData must be set"); } return new ConfirmationDialog(mPromptText, mExtraData); } } private ConfirmationDialog(CharSequence promptText, byte[] extraData) { mPromptText = promptText; mExtraData = extraData; } /** * Requests a confirmation prompt to be presented to the user. * * When the prompt is no longer being presented, one of the methods in * {@link ConfirmationCallback} is called on the supplied callback object. * * @param executor the executor identifying the thread that will receive the callback. * @param callback the callback to use when the dialog is done showing. * @throws IllegalArgumentException if the prompt text is too long or malfomed. * @throws ConfirmationAlreadyPresentingException if another prompt is being presented. * @throws ConfirmationNotAvailableException if confirmation prompts are not supported. */ public void presentPrompt(@NonNull Executor executor, @NonNull ConfirmationCallback callback) throws ConfirmationAlreadyPresentingException, ConfirmationNotAvailableException { if (mCallback != null) { throw new ConfirmationAlreadyPresentingException(); } mCallback = callback; mExecutor = executor; int uiOptionsAsFlags = 0; // TODO: set AccessibilityInverted, AccessibilityMagnified in uiOptionsAsFlags as needed. String locale = Locale.getDefault().toLanguageTag(); 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(); } } /** * Cancels a prompt currently being displayed. * * On success, the * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on * the supplied callback object will be called asynchronously. * * @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(); } else { // Unexpected error code. Log.w(TAG, "Unexpected responseCode=" + responseCode + " from cancelConfirmationPrompt() call."); throw new IllegalStateException(); } } /** * Checks if the device supports confirmation prompts. * * @return true if confirmation prompts are supported by the device. */ public static boolean isSupported() { // TODO: read and return system property. return true; } }
core/java/android/security/ConfirmationNotAvailableException.java 0 → 100644 +30 −0 Original line number Diff line number Diff line /* * Copyright 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; /** * This exception is thrown when presenting a prompt fails because the the environment lacks * facilities for showing confirmations. */ public class ConfirmationNotAvailableException extends Exception { public ConfirmationNotAvailableException() { } public ConfirmationNotAvailableException(String message) { super(message); } }