keystore: Add Trusted User Presence (TUP) APIs.
Test: m -j KeystoreTests && adb install -r
out/target/product/crosshatch/data/app/KeystoreTests/KeystoreTests.apk
adb shell am instrument
'android.security.tests/android.support.test.runner.AndroidJUnitRunner'
Bug: 72476834
Change-Id: I61ee4326a5e31f1cefacd47470b53634fa94c2ef
diff --git a/api/current.txt b/api/current.txt
index b869fe3..d0cc2b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38097,6 +38097,7 @@
method public boolean isInvalidatedByBiometricEnrollment();
method public boolean isRandomizedEncryptionRequired();
method public boolean isStrongBoxBacked();
+ method public boolean isTrustedUserPresenceRequired();
method public boolean isUserAuthenticationRequired();
method public boolean isUserAuthenticationValidWhileOnBody();
}
@@ -38122,6 +38123,7 @@
method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
+ method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
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);
@@ -38142,6 +38144,7 @@
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isInsideSecureHardware();
method public boolean isInvalidatedByBiometricEnrollment();
+ method public boolean isTrustedUserPresenceRequired();
method public boolean isUserAuthenticationRequired();
method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
method public boolean isUserAuthenticationValidWhileOnBody();
@@ -38242,6 +38245,12 @@
ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
}
+ public class UserPresenceUnavailableException extends java.security.InvalidAlgorithmParameterException {
+ ctor public UserPresenceUnavailableException();
+ ctor public UserPresenceUnavailableException(java.lang.String);
+ ctor public UserPresenceUnavailableException(java.lang.String, java.lang.Throwable);
+ }
+
public class WrappedKeyEntry implements java.security.KeyStore.Entry {
ctor public WrappedKeyEntry(byte[], java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec);
method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 379e177..f721ed3 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -290,6 +290,9 @@
spec.isUserAuthenticationValidWhileOnBody(),
spec.isInvalidatedByBiometricEnrollment(),
GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+ if (spec.isTrustedUserPresenceRequired()) {
+ args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+ }
KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
args,
mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index fdb885db..9df37f5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -177,6 +177,9 @@
&& (keymasterSwEnforcedUserAuthenticators == 0);
boolean userAuthenticationValidWhileOnBody =
keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
+ boolean trustedUserPresenceRequred =
+ keyCharacteristics.hwEnforced.getBoolean(
+ KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
boolean invalidatedByBiometricEnrollment = false;
if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_FINGERPRINT
@@ -203,6 +206,7 @@
(int) userAuthenticationValidityDurationSeconds,
userAuthenticationRequirementEnforcedBySecureHardware,
userAuthenticationValidWhileOnBody,
+ trustedUserPresenceRequred,
invalidatedByBiometricEnrollment);
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1e2b873..a896c72 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -258,6 +258,7 @@
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
+ private final boolean mTrustedUserPresenceRequred;
private final byte[] mAttestationChallenge;
private final boolean mUniqueIdIncluded;
private final boolean mUserAuthenticationValidWhileOnBody;
@@ -287,6 +288,7 @@
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
+ boolean trustedUserPresenceRequired,
byte[] attestationChallenge,
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
@@ -332,6 +334,7 @@
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
+ mTrustedUserPresenceRequred = trustedUserPresenceRequired;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
mUniqueIdIncluded = uniqueIdIncluded;
@@ -562,6 +565,14 @@
}
/**
+ * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+ * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+ */
+ public boolean isTrustedUserPresenceRequired() {
+ return mTrustedUserPresenceRequred;
+ }
+
+ /**
* Returns the attestation challenge value that will be placed in attestation certificate for
* this key pair.
*
@@ -658,6 +669,7 @@
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
+ private boolean mTrustedUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mUniqueIdIncluded = false;
private boolean mUserAuthenticationValidWhileOnBody;
@@ -718,6 +730,7 @@
mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
mUserAuthenticationValidityDurationSeconds =
sourceSpec.getUserAuthenticationValidityDurationSeconds();
+ mTrustedUserPresenceRequired = sourceSpec.isTrustedUserPresenceRequired();
mAttestationChallenge = sourceSpec.getAttestationChallenge();
mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
@@ -1095,6 +1108,16 @@
}
/**
+ * Sets whether a test of user presence is required to be performed between the
+ * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+ */
+ @NonNull
+ public Builder setTrustedUserPresenceRequired(boolean required) {
+ mTrustedUserPresenceRequired = required;
+ return this;
+ }
+
+ /**
* Sets whether an attestation certificate will be generated for this key pair, and what
* challenge value will be placed in the certificate. The attestation certificate chain
* can be retrieved with with {@link java.security.KeyStore#getCertificateChain(String)}.
@@ -1221,6 +1244,7 @@
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
mUserAuthenticationValidityDurationSeconds,
+ mTrustedUserPresenceRequired,
mAttestationChallenge,
mUniqueIdIncluded,
mUserAuthenticationValidWhileOnBody,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index f553319..864f62a 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -80,6 +80,7 @@
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
private final boolean mUserAuthenticationValidWhileOnBody;
+ private final boolean mTrustedUserPresenceRequired;
private final boolean mInvalidatedByBiometricEnrollment;
/**
@@ -101,6 +102,7 @@
int userAuthenticationValidityDurationSeconds,
boolean userAuthenticationRequirementEnforcedBySecureHardware,
boolean userAuthenticationValidWhileOnBody,
+ boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
@@ -121,6 +123,7 @@
mUserAuthenticationRequirementEnforcedBySecureHardware =
userAuthenticationRequirementEnforcedBySecureHardware;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
+ mTrustedUserPresenceRequired = trustedUserPresenceRequired;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
}
@@ -301,4 +304,12 @@
public boolean isInvalidatedByBiometricEnrollment() {
return mInvalidatedByBiometricEnrollment;
}
+
+ /**
+ * Returns {@code true} if the key can only be only be used if a test for user presence has
+ * succeeded since Signature.initSign() has been called.
+ */
+ public boolean isTrustedUserPresenceRequired() {
+ return mTrustedUserPresenceRequired;
+ }
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 7cb8e37..e5fdea7 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -101,6 +101,7 @@
out.writeBoolean(mSpec.isUniqueIdIncluded());
out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+ out.writeBoolean(mSpec.isTrustedUserPresenceRequired());
}
private static Date readDateOrNull(Parcel in) {
@@ -164,6 +165,7 @@
builder.setUniqueIdIncluded(in.readBoolean());
builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+ builder.setTrustedUserPresenceRequired(in.readBoolean());
mSpec = builder.build();
}
diff --git a/keystore/java/android/security/keystore/UserPresenceUnavailableException.java b/keystore/java/android/security/keystore/UserPresenceUnavailableException.java
new file mode 100644
index 0000000..cf4099e
--- /dev/null
+++ b/keystore/java/android/security/keystore/UserPresenceUnavailableException.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.InvalidAlgorithmParameterException;
+
+/**
+ * Indicates the condition that a proof of user-presence was
+ * requested but this proof was not presented.
+ */
+public class UserPresenceUnavailableException extends InvalidAlgorithmParameterException {
+ /**
+ * Constructs a {@code UserPresenceUnavailableException} without a detail message or cause.
+ */
+ public UserPresenceUnavailableException() {
+ super("No Strong Box available.");
+ }
+
+ /**
+ * Constructs a {@code UserPresenceUnavailableException} using the provided detail message
+ * but no cause.
+ */
+ public UserPresenceUnavailableException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a {@code UserPresenceUnavailableException} using the provided detail message
+ * and cause.
+ */
+ public UserPresenceUnavailableException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}