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:
David Zeuthen
2017-10-25 14:25:55 -04:00
parent f100eea32f
commit a8e8b659d0
14 changed files with 583 additions and 13 deletions

View File

@@ -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 {

View File

@@ -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);
}
}

View 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) {}
}

View 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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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());

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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);
}
}
}

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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;