Add Confirmation API.
This CL adds new Framework APIs that can be used for the secure confirmations. This includes support for configuring a key such that it can only sign data returned by the confirmation APIs. Bug: 63928580 Test: Manually tested. Change-Id: I94c1fc532376bd555b3dc37fc4709469450cfde6
This commit is contained in:
@@ -38192,6 +38192,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);
|
||||
@@ -38301,6 +38332,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 {
|
||||
@@ -38328,6 +38360,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 {
|
||||
@@ -38349,6 +38382,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 {
|
||||
@@ -38416,6 +38450,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 {
|
||||
@@ -38434,6 +38469,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 {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
54
core/java/android/security/ConfirmationCallback.java
Normal file
54
core/java/android/security/ConfirmationCallback.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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) {}
|
||||
}
|
||||
283
core/java/android/security/ConfirmationDialog.java
Normal file
283
core/java/android/security/ConfirmationDialog.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,7 @@ public final class KeymasterDefs {
|
||||
public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
|
||||
public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
|
||||
public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
|
||||
public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
|
||||
|
||||
public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
|
||||
public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
|
||||
|
||||
@@ -248,7 +248,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
||||
spec.getUserAuthenticationValidityDurationSeconds(),
|
||||
spec.isUserAuthenticationValidWhileOnBody(),
|
||||
spec.isInvalidatedByBiometricEnrollment(),
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
|
||||
spec.isUserConfirmationRequired());
|
||||
} catch (IllegalStateException | IllegalArgumentException e) {
|
||||
throw new InvalidAlgorithmParameterException(e);
|
||||
}
|
||||
@@ -289,7 +290,8 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
|
||||
spec.getUserAuthenticationValidityDurationSeconds(),
|
||||
spec.isUserAuthenticationValidWhileOnBody(),
|
||||
spec.isInvalidatedByBiometricEnrollment(),
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
|
||||
spec.isUserConfirmationRequired());
|
||||
if (spec.isTrustedUserPresenceRequired()) {
|
||||
args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
|
||||
}
|
||||
|
||||
@@ -349,7 +349,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
mSpec.getUserAuthenticationValidityDurationSeconds(),
|
||||
mSpec.isUserAuthenticationValidWhileOnBody(),
|
||||
mSpec.isInvalidatedByBiometricEnrollment(),
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
|
||||
mSpec.isUserConfirmationRequired());
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
throw new InvalidAlgorithmParameterException(e);
|
||||
}
|
||||
@@ -545,7 +546,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
|
||||
mSpec.getUserAuthenticationValidityDurationSeconds(),
|
||||
mSpec.isUserAuthenticationValidWhileOnBody(),
|
||||
mSpec.isInvalidatedByBiometricEnrollment(),
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
|
||||
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
|
||||
mSpec.isUserConfirmationRequired());
|
||||
args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
|
||||
args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
|
||||
mSpec.getKeyValidityForOriginationEnd());
|
||||
|
||||
@@ -190,6 +190,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
&& !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
|
||||
}
|
||||
|
||||
boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
|
||||
|
||||
return new KeyInfo(entryAlias,
|
||||
insideSecureHardware,
|
||||
origin,
|
||||
@@ -207,7 +209,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
userAuthenticationRequirementEnforcedBySecureHardware,
|
||||
userAuthenticationValidWhileOnBody,
|
||||
trustedUserPresenceRequred,
|
||||
invalidatedByBiometricEnrollment);
|
||||
invalidatedByBiometricEnrollment,
|
||||
userConfirmationRequired);
|
||||
}
|
||||
|
||||
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
|
||||
|
||||
@@ -502,7 +502,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
spec.getUserAuthenticationValidityDurationSeconds(),
|
||||
spec.isUserAuthenticationValidWhileOnBody(),
|
||||
spec.isInvalidatedByBiometricEnrollment(),
|
||||
spec.getBoundToSpecificSecureUserId());
|
||||
spec.getBoundToSpecificSecureUserId(),
|
||||
spec.isUserConfirmationRequired());
|
||||
importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
|
||||
spec.getKeyValidityStart());
|
||||
importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
|
||||
@@ -704,7 +705,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
|
||||
params.getUserAuthenticationValidityDurationSeconds(),
|
||||
params.isUserAuthenticationValidWhileOnBody(),
|
||||
params.isInvalidatedByBiometricEnrollment(),
|
||||
params.getBoundToSpecificSecureUserId());
|
||||
params.getBoundToSpecificSecureUserId(),
|
||||
params.isUserConfirmationRequired());
|
||||
KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
|
||||
args,
|
||||
keymasterAlgorithm,
|
||||
|
||||
@@ -264,6 +264,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
private final boolean mUserAuthenticationValidWhileOnBody;
|
||||
private final boolean mInvalidatedByBiometricEnrollment;
|
||||
private final boolean mIsStrongBoxBacked;
|
||||
private final boolean mUserConfirmationRequired;
|
||||
|
||||
/**
|
||||
* @hide should be built with Builder
|
||||
@@ -293,7 +294,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
boolean uniqueIdIncluded,
|
||||
boolean userAuthenticationValidWhileOnBody,
|
||||
boolean invalidatedByBiometricEnrollment,
|
||||
boolean isStrongBoxBacked) {
|
||||
boolean isStrongBoxBacked,
|
||||
boolean userConfirmationRequired) {
|
||||
if (TextUtils.isEmpty(keyStoreAlias)) {
|
||||
throw new IllegalArgumentException("keyStoreAlias must not be empty");
|
||||
}
|
||||
@@ -341,6 +343,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
|
||||
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
|
||||
mIsStrongBoxBacked = isStrongBoxBacked;
|
||||
mUserConfirmationRequired = userConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,6 +549,26 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
return mUserAuthenticationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the key is authorized to be used only for messages confirmed by the
|
||||
* user.
|
||||
*
|
||||
* Confirmation is separate from user authentication (see
|
||||
* {@link Builder#setUserAuthenticationRequired(boolean)}). Keys can be created that require
|
||||
* confirmation but not user authentication, or user authentication but not confirmation, or
|
||||
* both. Confirmation verifies that some user with physical possession of the device has
|
||||
* approved a displayed message. User authentication verifies that the correct user is present
|
||||
* and has authenticated.
|
||||
*
|
||||
* <p>This authorization applies only to secret key and private key operations. Public key
|
||||
* operations are not restricted.
|
||||
*
|
||||
* @see Builder#setUserConfirmationRequired(boolean)
|
||||
*/
|
||||
public boolean isUserConfirmationRequired() {
|
||||
return mUserConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of time (seconds) for which this key is authorized to be used after the
|
||||
* user is successfully authenticated. This has effect only if user authentication is required
|
||||
@@ -675,6 +698,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
private boolean mUserAuthenticationValidWhileOnBody;
|
||||
private boolean mInvalidatedByBiometricEnrollment = true;
|
||||
private boolean mIsStrongBoxBacked = false;
|
||||
private boolean mUserConfirmationRequired;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the {@code Builder}.
|
||||
@@ -1062,6 +1086,29 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this key is authorized to be used only for messages confirmed by the
|
||||
* user.
|
||||
*
|
||||
* Confirmation is separate from user authentication (see
|
||||
* {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
|
||||
* confirmation but not user authentication, or user authentication but not confirmation,
|
||||
* or both. Confirmation verifies that some user with physical possession of the device has
|
||||
* approved a displayed message. User authentication verifies that the correct user is
|
||||
* present and has authenticated.
|
||||
*
|
||||
* <p>This authorization applies only to secret key and private key operations. Public key
|
||||
* operations are not restricted.
|
||||
*
|
||||
* @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
|
||||
* more details about user confirmations.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setUserConfirmationRequired(boolean required) {
|
||||
mUserConfirmationRequired = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration of time (seconds) for which this key is authorized to be used after the
|
||||
* user is successfully authenticated. This has effect if the key requires user
|
||||
@@ -1249,7 +1296,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
|
||||
mUniqueIdIncluded,
|
||||
mUserAuthenticationValidWhileOnBody,
|
||||
mInvalidatedByBiometricEnrollment,
|
||||
mIsStrongBoxBacked);
|
||||
mIsStrongBoxBacked,
|
||||
mUserConfirmationRequired);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public class KeyInfo implements KeySpec {
|
||||
private final boolean mUserAuthenticationValidWhileOnBody;
|
||||
private final boolean mTrustedUserPresenceRequired;
|
||||
private final boolean mInvalidatedByBiometricEnrollment;
|
||||
private final boolean mUserConfirmationRequired;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
@@ -103,7 +104,8 @@ public class KeyInfo implements KeySpec {
|
||||
boolean userAuthenticationRequirementEnforcedBySecureHardware,
|
||||
boolean userAuthenticationValidWhileOnBody,
|
||||
boolean trustedUserPresenceRequired,
|
||||
boolean invalidatedByBiometricEnrollment) {
|
||||
boolean invalidatedByBiometricEnrollment,
|
||||
boolean userConfirmationRequired) {
|
||||
mKeystoreAlias = keystoreKeyAlias;
|
||||
mInsideSecureHardware = insideSecureHardware;
|
||||
mOrigin = origin;
|
||||
@@ -125,6 +127,7 @@ public class KeyInfo implements KeySpec {
|
||||
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
|
||||
mTrustedUserPresenceRequired = trustedUserPresenceRequired;
|
||||
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
|
||||
mUserConfirmationRequired = userConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,6 +262,27 @@ public class KeyInfo implements KeySpec {
|
||||
return mUserAuthenticationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the key is authorized to be used only for messages confirmed by the
|
||||
* user.
|
||||
*
|
||||
* Confirmation is separate from user authentication (see
|
||||
* {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
|
||||
* not user authentication, or user authentication but not confirmation, or both. Confirmation
|
||||
* verifies that some user with physical possession of the device has approved a displayed
|
||||
* message. User authentication verifies that the correct user is present and has
|
||||
* authenticated.
|
||||
*
|
||||
* <p>This authorization applies only to secret key and private key operations. Public key
|
||||
* operations are not restricted.
|
||||
*
|
||||
* @see KeyGenParameterSpec.Builder#setUserConfirmationRequired(boolean)
|
||||
* @see KeyProtection.Builder#setUserConfirmationRequired(boolean)
|
||||
*/
|
||||
public boolean isUserConfirmationRequired() {
|
||||
return mUserConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of time (seconds) for which this key is authorized to be used after the
|
||||
* user is successfully authenticated. This has effect only if user authentication is required
|
||||
|
||||
@@ -228,6 +228,7 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
private final boolean mInvalidatedByBiometricEnrollment;
|
||||
private final long mBoundToSecureUserId;
|
||||
private final boolean mCriticalToDeviceEncryption;
|
||||
private final boolean mUserConfirmationRequired;
|
||||
|
||||
private KeyProtection(
|
||||
Date keyValidityStart,
|
||||
@@ -244,7 +245,8 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
boolean userAuthenticationValidWhileOnBody,
|
||||
boolean invalidatedByBiometricEnrollment,
|
||||
long boundToSecureUserId,
|
||||
boolean criticalToDeviceEncryption) {
|
||||
boolean criticalToDeviceEncryption,
|
||||
boolean userConfirmationRequired) {
|
||||
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
|
||||
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
|
||||
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
|
||||
@@ -262,6 +264,7 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
|
||||
mBoundToSecureUserId = boundToSecureUserId;
|
||||
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
|
||||
mUserConfirmationRequired = userConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,6 +398,26 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
return mUserAuthenticationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the key is authorized to be used only for messages confirmed by the
|
||||
* user.
|
||||
*
|
||||
* Confirmation is separate from user authentication (see
|
||||
* {@link #isUserAuthenticationRequired()}). Keys can be created that require confirmation but
|
||||
* not user authentication, or user authentication but not confirmation, or both. Confirmation
|
||||
* verifies that some user with physical possession of the device has approved a displayed
|
||||
* message. User authentication verifies that the correct user is present and has
|
||||
* authenticated.
|
||||
*
|
||||
* <p>This authorization applies only to secret key and private key operations. Public key
|
||||
* operations are not restricted.
|
||||
*
|
||||
* @see Builder#setUserConfirmationRequired(boolean)
|
||||
*/
|
||||
public boolean isUserConfirmationRequired() {
|
||||
return mUserConfirmationRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of time (seconds) for which this key is authorized to be used after the
|
||||
* user is successfully authenticated. This has effect only if user authentication is required
|
||||
@@ -488,6 +511,7 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
private int mUserAuthenticationValidityDurationSeconds = -1;
|
||||
private boolean mUserAuthenticationValidWhileOnBody;
|
||||
private boolean mInvalidatedByBiometricEnrollment = true;
|
||||
private boolean mUserConfirmationRequired;
|
||||
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
|
||||
private boolean mCriticalToDeviceEncryption = false;
|
||||
|
||||
@@ -718,6 +742,29 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this key is authorized to be used only for messages confirmed by the
|
||||
* user.
|
||||
*
|
||||
* Confirmation is separate from user authentication (see
|
||||
* {@link #setUserAuthenticationRequired(boolean)}). Keys can be created that require
|
||||
* confirmation but not user authentication, or user authentication but not confirmation,
|
||||
* or both. Confirmation verifies that some user with physical possession of the device has
|
||||
* approved a displayed message. User authentication verifies that the correct user is
|
||||
* present and has authenticated.
|
||||
*
|
||||
* <p>This authorization applies only to secret key and private key operations. Public key
|
||||
* operations are not restricted.
|
||||
*
|
||||
* @see {@link android.security.ConfirmationPrompter ConfirmationPrompter} class for
|
||||
* more details about user confirmations.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setUserConfirmationRequired(boolean required) {
|
||||
mUserConfirmationRequired = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the duration of time (seconds) for which this key is authorized to be used after the
|
||||
* user is successfully authenticated. This has effect if the key requires user
|
||||
@@ -866,7 +913,8 @@ public final class KeyProtection implements ProtectionParameter {
|
||||
mUserAuthenticationValidWhileOnBody,
|
||||
mInvalidatedByBiometricEnrollment,
|
||||
mBoundToSecureUserId,
|
||||
mCriticalToDeviceEncryption);
|
||||
mCriticalToDeviceEncryption,
|
||||
mUserConfirmationRequired);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.security.keystore;
|
||||
|
||||
import android.util.Log;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.security.GateKeeper;
|
||||
import android.security.KeyStore;
|
||||
@@ -93,6 +94,8 @@ public abstract class KeymasterUtils {
|
||||
* overriding the default logic in this method where the key is bound to either the root
|
||||
* SID of the current user, or the fingerprint SID if explicit fingerprint authorization
|
||||
* is requested.
|
||||
* @param userConfirmationRequired whether user confirmation is required to authorize the use
|
||||
* of the key.
|
||||
* @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.
|
||||
@@ -102,7 +105,12 @@ public abstract class KeymasterUtils {
|
||||
int userAuthenticationValidityDurationSeconds,
|
||||
boolean userAuthenticationValidWhileOnBody,
|
||||
boolean invalidatedByBiometricEnrollment,
|
||||
long boundToSpecificSecureUserId) {
|
||||
long boundToSpecificSecureUserId,
|
||||
boolean userConfirmationRequired) {
|
||||
if (userConfirmationRequired) {
|
||||
args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
|
||||
}
|
||||
|
||||
if (!userAuthenticationRequired) {
|
||||
args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user