summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt8
-rw-r--r--core/java/android/content/pm/PackageManager.java18
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java1
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java3
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java64
-rw-r--r--keystore/java/android/security/keystore/KeyInfo.java16
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java5
-rw-r--r--keystore/java/android/security/keystore/KeyProtection.java61
-rw-r--r--keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java5
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java7
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java7
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java14
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java12
13 files changed, 212 insertions, 9 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index f453ccaf75ad..94f4d995bde9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12134,6 +12134,8 @@ package android.content.pm {
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
+ field public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY = "android.hardware.keystore.limited_use_key";
+ field public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY = "android.hardware.keystore.single_use_key";
field public static final String FEATURE_LEANBACK = "android.software.leanback";
field public static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -36205,6 +36207,7 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
method @NonNull public String getKeystoreAlias();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36241,6 +36244,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
@@ -36263,6 +36267,7 @@ package android.security.keystore {
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getRemainingUsageCount();
method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36332,6 +36337,7 @@ package android.security.keystore {
field public static final int SECURITY_LEVEL_UNKNOWN_SECURE = -1; // 0xffffffff
field public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
field public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
+ field public static final int UNRESTRICTED_USAGE_COUNT = -1; // 0xffffffff
}
public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
@@ -36341,6 +36347,7 @@ package android.security.keystore {
method @Nullable public java.util.Date getKeyValidityForConsumptionEnd();
method @Nullable public java.util.Date getKeyValidityForOriginationEnd();
method @Nullable public java.util.Date getKeyValidityStart();
+ method public int getMaxUsageCount();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
@@ -36366,6 +36373,7 @@ package android.security.keystore {
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
method @NonNull public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
+ method @NonNull public android.security.keystore.KeyProtection.Builder setMaxUsageCount(int);
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 31beb6e6a565..742f48c7a6a3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3224,6 +3224,24 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_APP_ENUMERATION = "android.software.app_enumeration";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can only enforce limited use key in hardware with max usage
+ * count equals to 1.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_SINGLE_USE_KEY =
+ "android.hardware.keystore.single_use_key";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+ * a Keystore implementation that can enforce limited use key in hardware with any max usage
+ * count (including count equals to 1).
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_KEYSTORE_LIMITED_USE_KEY =
+ "android.hardware.keystore.limited_use_key";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f994d2930cd9..c39b8c5eb6d1 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -80,6 +80,7 @@ public final class KeymasterDefs {
public static final int KM_TAG_MIN_SECONDS_BETWEEN_OPS =
Tag.MIN_SECONDS_BETWEEN_OPS; // KM_UINT | 403;
public static final int KM_TAG_MAX_USES_PER_BOOT = Tag.MAX_USES_PER_BOOT; // KM_UINT | 404;
+ public static final int KM_TAG_USAGE_COUNT_LIMIT = Tag.USAGE_COUNT_LIMIT; // KM_UINT | 405;
public static final int KM_TAG_USER_ID = Tag.USER_ID; // KM_UINT | 501;
public static final int KM_TAG_USER_SECURE_ID = Tag.USER_SECURE_ID; // KM_ULONG_REP | 502;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 3694d635422f..d2678c71813c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -215,7 +215,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
// Keystore 1.0 does not tell us the exact security level of the key
// so we report an unknown but secure security level.
insideSecureHardware ? KeyProperties.SECURITY_LEVEL_UNKNOWN_SECURE
- : KeyProperties.SECURITY_LEVEL_SOFTWARE);
+ : KeyProperties.SECURITY_LEVEL_SOFTWARE,
+ KeyProperties.UNRESTRICTED_USAGE_COUNT);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index e9aac7ddb56d..c2a7b2ee6323 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -274,6 +274,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mCriticalToDeviceEncryption;
+ private final int mMaxUsageCount;
/*
* ***NOTE***: All new fields MUST also be added to the following:
* ParcelableKeyGenParameterSpec class.
@@ -313,7 +314,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
boolean isStrongBoxBacked,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean criticalToDeviceEncryption) {
+ boolean criticalToDeviceEncryption,
+ int maxUsageCount) {
if (TextUtils.isEmpty(keyStoreAlias)) {
throw new IllegalArgumentException("keyStoreAlias must not be empty");
}
@@ -366,6 +368,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -782,7 +785,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
- * Return whether this key is critical to the device encryption flow.
+ * Returns whether this key is critical to the device encryption flow.
*
* @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
@@ -792,6 +795,17 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyGenParameterSpec} instances.
*/
public final static class Builder {
@@ -827,6 +841,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
private boolean mUserConfirmationRequired;
private boolean mUnlockedDeviceRequired = false;
private boolean mCriticalToDeviceEncryption = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -894,6 +909,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mUserConfirmationRequired = sourceSpec.isUserConfirmationRequired();
mUnlockedDeviceRequired = sourceSpec.isUnlockedDeviceRequired();
mCriticalToDeviceEncryption = sourceSpec.isCriticalToDeviceEncryption();
+ mMaxUsageCount = sourceSpec.getMaxUsageCount();
}
/**
@@ -1553,6 +1569,47 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@code KeyGenParameterSpec}.
*/
@NonNull
@@ -1587,7 +1644,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu
mIsStrongBoxBacked,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mCriticalToDeviceEncryption);
+ mCriticalToDeviceEncryption,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 7158d0cf248e..f50efd2c3328 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -85,6 +85,7 @@ public class KeyInfo implements KeySpec {
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mUserConfirmationRequired;
private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
+ private final int mRemainingUsageCount;
/**
* @hide
@@ -109,7 +110,8 @@ public class KeyInfo implements KeySpec {
boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment,
boolean userConfirmationRequired,
- @KeyProperties.SecurityLevelEnum int securityLevel) {
+ @KeyProperties.SecurityLevelEnum int securityLevel,
+ int remainingUsageCount) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -134,6 +136,7 @@ public class KeyInfo implements KeySpec {
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mUserConfirmationRequired = userConfirmationRequired;
mSecurityLevel = securityLevel;
+ mRemainingUsageCount = remainingUsageCount;
}
/**
@@ -374,4 +377,15 @@ public class KeyInfo implements KeySpec {
public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
return mSecurityLevel;
}
+
+ /**
+ * Returns the remaining number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there's no restriction on the number of
+ * times the key can be used. Note that this gives a best effort count and need not be
+ * accurate (as there might be usages happening in parallel and the count maintained here need
+ * not be in sync with the usage).
+ */
+ public int getRemainingUsageCount() {
+ return mRemainingUsageCount;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 014d6882be8d..fa4f8b1674d1 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -907,4 +907,9 @@ public abstract class KeyProperties {
+ uid);
}
}
+
+ /**
+ * This value indicates that there is no restriction on the number of times the key can be used.
+ */
+ public static final int UNRESTRICTED_USAGE_COUNT = -1;
}
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2e793dea3e05..76ce23efd05b 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -235,6 +235,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private final boolean mUserConfirmationRequired;
private final boolean mUnlockedDeviceRequired;
private final boolean mIsStrongBoxBacked;
+ private final int mMaxUsageCount;
private KeyProtection(
Date keyValidityStart,
@@ -256,7 +257,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
boolean criticalToDeviceEncryption,
boolean userConfirmationRequired,
boolean unlockedDeviceRequired,
- boolean isStrongBoxBacked) {
+ boolean isStrongBoxBacked,
+ int maxUsageCount) {
mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -279,6 +281,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
mUserConfirmationRequired = userConfirmationRequired;
mUnlockedDeviceRequired = unlockedDeviceRequired;
mIsStrongBoxBacked = isStrongBoxBacked;
+ mMaxUsageCount = maxUsageCount;
}
/**
@@ -548,6 +551,17 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
}
/**
+ * Returns the maximum number of times the limited use key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there’s no restriction on the number of
+ * times the key can be used.
+ *
+ * @see Builder#setMaxUsageCount(int)
+ */
+ public int getMaxUsageCount() {
+ return mMaxUsageCount;
+ }
+
+ /**
* Builder of {@link KeyProtection} instances.
*/
public final static class Builder {
@@ -574,6 +588,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
private boolean mCriticalToDeviceEncryption = false;
private boolean mIsStrongBoxBacked = false;
+ private int mMaxUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
/**
* Creates a new instance of the {@code Builder}.
@@ -1040,6 +1055,47 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
}
/**
+ * Sets the maximum number of times the key is allowed to be used. After every use of the
+ * key, the use counter will decrease. This authorization applies only to secret key and
+ * private key operations. Public key operations are not restricted. For example, after
+ * successfully encrypting and decrypting data using methods such as
+ * {@link Cipher#doFinal()}, the use counter of the secret key will decrease. After
+ * successfully signing data using methods such as {@link Signature#sign()}, the use
+ * counter of the private key will decrease.
+ *
+ * When the use counter is depleted, the key will be marked for deletion by Android
+ * Keystore and any subsequent attempt to use the key will throw
+ * {@link KeyPermanentlyInvalidatedException}. There is no key to be loaded from the
+ * Android Keystore once the exhausted key is permanently deleted, as if the key never
+ * existed before.
+ *
+ * <p>By default, there is no restriction on the usage of key.
+ *
+ * <p>Some secure hardware may not support this feature at all, in which case it will
+ * be enforced in software, some secure hardware may support it but only with
+ * maxUsageCount = 1, and some secure hardware may support it with larger value
+ * of maxUsageCount.
+ *
+ * <p>The PackageManger feature flags:
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_SINGLE_USE_KEY} and
+ * {@link android.content.pm.PackageManager#FEATURE_KEYSTORE_LIMITED_USE_KEY} can be used
+ * to check whether the secure hardware cannot enforce this feature, can only enforce it
+ * with maxUsageCount = 1, or can enforce it with larger value of maxUsageCount.
+ *
+ * @param maxUsageCount maximum number of times the key is allowed to be used or
+ * {@link KeyProperties#UNRESTRICTED_USAGE_COUNT} if there is no restriction on the
+ * usage.
+ */
+ @NonNull
+ public Builder setMaxUsageCount(int maxUsageCount) {
+ if (maxUsageCount == KeyProperties.UNRESTRICTED_USAGE_COUNT || maxUsageCount > 0) {
+ mMaxUsageCount = maxUsageCount;
+ return this;
+ }
+ throw new IllegalArgumentException("maxUsageCount is not valid");
+ }
+
+ /**
* Builds an instance of {@link KeyProtection}.
*
* @throws IllegalArgumentException if a required field is missing
@@ -1066,7 +1122,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
mCriticalToDeviceEncryption,
mUserConfirmationRequired,
mUnlockedDeviceRequired,
- mIsStrongBoxBacked);
+ mIsStrongBoxBacked,
+ mMaxUsageCount);
}
}
}
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 69c15cca68bf..8163472abdfb 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -108,6 +108,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
out.writeBoolean(mSpec.isUserConfirmationRequired());
out.writeBoolean(mSpec.isUnlockedDeviceRequired());
out.writeBoolean(mSpec.isCriticalToDeviceEncryption());
+ out.writeInt(mSpec.getMaxUsageCount());
}
private static Date readDateOrNull(Parcel in) {
@@ -166,6 +167,7 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
final boolean userConfirmationRequired = in.readBoolean();
final boolean unlockedDeviceRequired = in.readBoolean();
final boolean criticalToDeviceEncryption = in.readBoolean();
+ final int maxUsageCount = in.readInt();
// The KeyGenParameterSpec is intentionally not constructed using a Builder here:
// The intention is for this class to break if new parameters are added to the
// KeyGenParameterSpec constructor (whereas using a builder would silently drop them).
@@ -199,7 +201,8 @@ public final class ParcelableKeyGenParameterSpec implements Parcelable {
isStrongBoxBacked,
userConfirmationRequired,
unlockedDeviceRequired,
- criticalToDeviceEncryption);
+ criticalToDeviceEncryption,
+ maxUsageCount);
}
public static final @android.annotation.NonNull Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
index 233f352989ab..1575bb411562 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java
@@ -366,6 +366,13 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
+
byte[] additionalEntropy =
KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, (mKeySizeBits + 7) / 8);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index df0e1462a492..6a92980de37c 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -586,6 +586,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
));
}
+ if (mSpec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ params.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ mSpec.getMaxUsageCount()
+ ));
+ }
+
addAlgorithmSpecificParameters(params);
if (mSpec.isUniqueIdIncluded()) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 74503e1eecfa..fe05989c3846 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -95,6 +95,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
boolean userAuthenticationValidWhileOnBody = false;
boolean trustedUserPresenceRequired = false;
boolean trustedUserConfirmationRequired = false;
+ int remainingUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT;
try {
for (Authorization a : key.getAuthorizations()) {
switch (a.keyParameter.tag) {
@@ -195,6 +196,16 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
trustedUserConfirmationRequired =
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
break;
+ case KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT:
+ long remainingUsageCountUnsigned =
+ KeyStore2ParameterUtils.getUnsignedInt(a);
+ if (remainingUsageCountUnsigned > Integer.MAX_VALUE) {
+ throw new ProviderException(
+ "Usage count of limited use key too long: "
+ + remainingUsageCountUnsigned);
+ }
+ remainingUsageCount = (int) remainingUsageCountUnsigned;
+ break;
}
}
} catch (IllegalArgumentException e) {
@@ -247,7 +258,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
trustedUserConfirmationRequired,
- securityLevel);
+ securityLevel,
+ remainingUsageCount);
}
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index 07169cedc1d9..8c8acc418a0e 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -535,6 +535,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
spec.getKeyValidityForConsumptionEnd()
));
}
+ if (spec.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ spec.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}
@@ -772,6 +778,12 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
params.getKeyValidityForConsumptionEnd()
));
}
+ if (params.getMaxUsageCount() != KeyProperties.UNRESTRICTED_USAGE_COUNT) {
+ importArgs.add(KeyStore2ParameterUtils.makeInt(
+ KeymasterDefs.KM_TAG_USAGE_COUNT_LIMIT,
+ params.getMaxUsageCount()
+ ));
+ }
} catch (IllegalArgumentException | IllegalStateException e) {
throw new KeyStoreException(e);
}