From cd6c58acd83c7a77783258f65dc99a0455e290d2 Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Sat, 16 Jan 2021 23:26:30 +0800 Subject: [PATCH 1/2] Limited use key: feature flags. Add feature flags to indicate whether KeyMint only enforce this feature in hardware with count == 1 or enforce with any number of the count (>=1). Test: compile Change-Id: Id9c13ee91b779164bd07ff20ae2b4c0e158fdb33 Merged-In: Id9c13ee91b779164bd07ff20ae2b4c0e158fdb33 --- core/api/current.txt | 2 ++ .../android/content/pm/PackageManager.java | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/core/api/current.txt b/core/api/current.txt index 285506f0556d3..e9f2d7bdf8d02 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12133,6 +12133,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"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 31beb6e6a5650..742f48c7a6a31 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; From cca3d8120440b0c86c8389818583da1df067d7fc Mon Sep 17 00:00:00 2001 From: Qi Wu Date: Fri, 27 Nov 2020 22:38:23 +0800 Subject: [PATCH 2/2] Add limited use keys related API into Keystore 2.0 SPI. Bug: b/174140443 Test: atest CtsKeystoreTestCases The new CTS tests for this feature is introduced in aosp/1556464 Change-Id: I9620c4a3e5d2c10ed8a50d494e63eb2fb19dabef Merged-In: I9620c4a3e5d2c10ed8a50d494e63eb2fb19dabef --- core/api/current.txt | 6 ++ .../security/keymaster/KeymasterDefs.java | 1 + .../AndroidKeyStoreSecretKeyFactorySpi.java | 3 +- .../keystore/KeyGenParameterSpec.java | 64 ++++++++++++++++++- .../android/security/keystore/KeyInfo.java | 16 ++++- .../security/keystore/KeyProperties.java | 5 ++ .../security/keystore/KeyProtection.java | 61 +++++++++++++++++- .../ParcelableKeyGenParameterSpec.java | 5 +- .../AndroidKeyStoreKeyGeneratorSpi.java | 7 ++ .../AndroidKeyStoreKeyPairGeneratorSpi.java | 7 ++ .../AndroidKeyStoreSecretKeyFactorySpi.java | 14 +++- .../keystore2/AndroidKeyStoreSpi.java | 12 ++++ 12 files changed, 192 insertions(+), 9 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index e9f2d7bdf8d02..1daca3e20249f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -36206,6 +36206,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(); @@ -36242,6 +36243,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); @@ -36264,6 +36266,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(); @@ -36333,6 +36336,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 { @@ -36342,6 +36346,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(); @@ -36367,6 +36372,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/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index f994d2930cd9d..c39b8c5eb6d12 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 3694d635422fe..d2678c71813c8 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 e9aac7ddb56df..c2a7b2ee6323e 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 @@ -791,6 +794,17 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return mCriticalToDeviceEncryption; } + /** + * 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. */ @@ -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(); } /** @@ -1552,6 +1568,47 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu return this; } + /** + * 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. + * + *

By default, there is no restriction on the usage of key. + * + *

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. + * + *

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}. */ @@ -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 7158d0cf248eb..f50efd2c3328e 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 014d6882be8d1..fa4f8b1674d1a 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 2e793dea3e05d..76ce23efd05b4 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; } /** @@ -547,6 +550,17 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { return mIsStrongBoxBacked; } + /** + * 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. */ @@ -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}. @@ -1039,6 +1054,47 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { return this; } + /** + * 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. + * + *

By default, there is no restriction on the usage of key. + * + *

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. + * + *

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}. * @@ -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 69c15cca68bff..8163472abdfb7 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 CREATOR = new Creator() { diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyGeneratorSpi.java index 233f352989ab7..1575bb411562f 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 df0e1462a4925..6a92980de37c1 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 74503e1eecfa8..fe05989c38467 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 07169cedc1d95..8c8acc418a0e8 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); }