Keystore 2.0 SPI: Update KeyInfo and KeyGenParameterSpec
This patch adds set/getSecurityLevel to KeyInfo and KeyGenParameterSpec
and it deprecates the superseded function isInSecureHardware.
It also deprecates the system API set/getUid and replaces it with the
more generic set/getNamespace.
Test: None
Change-Id: Id2f54596510954862b5077a935f3daf07211f29c
diff --git a/api/current.txt b/api/current.txt
index 560b5f6..a584f40 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -42808,10 +42808,11 @@
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
- method public boolean isInsideSecureHardware();
+ method @Deprecated public boolean isInsideSecureHardware();
method public boolean isInvalidatedByBiometricEnrollment();
method public boolean isTrustedUserPresenceRequired();
method public boolean isUserAuthenticationRequired();
diff --git a/api/system-current.txt b/api/system-current.txt
index 85d7930..cb94dae 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9374,8 +9374,13 @@
ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable);
}
+ public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+ method public int getNamespace();
+ }
+
public static final class KeyGenParameterSpec.Builder {
- method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
+ method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 0aa24cf..9158f9e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40976,10 +40976,11 @@
method public String getKeystoreAlias();
method public int getOrigin();
method public int getPurposes();
+ method public int getSecurityLevel();
method @NonNull public String[] getSignaturePaddings();
method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
- method public boolean isInsideSecureHardware();
+ method @Deprecated public boolean isInsideSecureHardware();
method public boolean isInvalidatedByBiometricEnrollment();
method public boolean isTrustedUserPresenceRequired();
method public boolean isUserAuthenticationRequired();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 25f72bd..2a6f1ee 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8256,8 +8256,13 @@
ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable);
}
+ public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
+ method public int getNamespace();
+ }
+
public static final class KeyGenParameterSpec.Builder {
- method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setNamespace(int);
+ method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUid(int);
}
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 71e6559..d1b4464 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -23,6 +23,7 @@
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterDefs;
+import android.sysprop.Keystore2Properties;
import java.io.IOException;
import java.security.KeyFactory;
@@ -111,6 +112,26 @@
putSecretKeyFactoryImpl("HmacSHA512");
}
+ private static boolean sKeystore2Enabled;
+
+ /**
+ * This function indicates whether or not Keystore 2.0 is enabled. Some parts of the
+ * Keystore SPI must behave subtly differently when Keystore 2.0 is enabled. However,
+ * the platform property that indicates that Keystore 2.0 is enabled is not readable
+ * by applications. So we set this value when {@code install()} is called because it
+ * is called by zygote, which can access Keystore2Properties.
+ *
+ * This function can be removed once the transition to Keystore 2.0 is complete.
+ * b/171305684
+ *
+ * @return true if Keystore 2.0 is enabled.
+ * @hide
+ */
+ public static boolean isKeystore2Enabled() {
+ return sKeystore2Enabled;
+ }
+
+
/**
* Installs a new instance of this provider (and the
* {@link AndroidKeyStoreBCWorkaroundProvider}).
@@ -138,6 +159,11 @@
// priority.
Security.addProvider(workaroundProvider);
}
+
+ // {@code install()} is run by zygote when this property is still accessible. We store its
+ // value so that the Keystore SPI can act accordingly without having to access an internal
+ // property.
+ sKeystore2Enabled = Keystore2Properties.keystore2_enabled().orElse(false);
}
private void putSecretKeyFactoryImpl(String algorithm) {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 9707260..3694d63 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -211,7 +211,11 @@
userAuthenticationValidWhileOnBody,
trustedUserPresenceRequired,
invalidatedByBiometricEnrollment,
- userConfirmationRequired);
+ userConfirmationRequired,
+ // 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);
}
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 688c4a7..e9aac7d 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -27,7 +27,6 @@
import android.hardware.biometrics.BiometricPrompt;
import android.os.Build;
import android.security.GateKeeper;
-import android.security.KeyStore;
import android.text.TextUtils;
import java.math.BigInteger;
@@ -246,7 +245,7 @@
private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
private final String mKeystoreAlias;
- private final int mUid;
+ private final int mNamespace;
private final int mKeySize;
private final AlgorithmParameterSpec mSpec;
private final X500Principal mCertificateSubject;
@@ -286,7 +285,7 @@
*/
public KeyGenParameterSpec(
String keyStoreAlias,
- int uid,
+ int namespace,
int keySize,
AlgorithmParameterSpec spec,
X500Principal certificateSubject,
@@ -337,7 +336,7 @@
}
mKeystoreAlias = keyStoreAlias;
- mUid = uid;
+ mNamespace = namespace;
mKeySize = keySize;
mSpec = spec;
mCertificateSubject = certificateSubject;
@@ -382,11 +381,43 @@
* Returns the UID which will own the key. {@code -1} is an alias for the UID of the current
* process.
*
+ * @deprecated See deprecation message on {@link KeyGenParameterSpec.Builder#setUid(int)}.
+ * Known namespaces will be translated to their legacy UIDs. Unknown
+ * Namespaces will yield {@link IllegalStateException}.
+ *
* @hide
*/
@UnsupportedAppUsage
+ @Deprecated
public int getUid() {
- return mUid;
+ if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
+ // a UID, because we are still being used with the old Keystore SPI.
+ // TODO This if statement and body can be removed when the Keystore 2 migration is
+ // complete. b/171563717
+ return mNamespace;
+ }
+ try {
+ return KeyProperties.namespaceToLegacyUid(mNamespace);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalStateException("getUid called on KeyGenParameterSpec with non legacy"
+ + " keystore namespace.", e);
+ }
+ }
+
+ /**
+ * Returns the target namespace for the key.
+ * See {@link KeyGenParameterSpec.Builder#setNamespace(int)}.
+ *
+ * @return The numeric namespace as configured in the keystore2_key_contexts files of Android's
+ * SEPolicy.
+ * TODO b/171806779 link to public Keystore 2.0 documentation.
+ * See bug for more details for now.
+ * @hide
+ */
+ @SystemApi
+ public int getNamespace() {
+ return mNamespace;
}
/**
@@ -767,7 +798,7 @@
private final String mKeystoreAlias;
private @KeyProperties.PurposeEnum int mPurposes;
- private int mUid = KeyStore.UID_SELF;
+ private int mNamespace = KeyProperties.NAMESPACE_APPLICATION;
private int mKeySize = -1;
private AlgorithmParameterSpec mSpec;
private X500Principal mCertificateSubject;
@@ -830,7 +861,7 @@
*/
public Builder(@NonNull KeyGenParameterSpec sourceSpec) {
this(sourceSpec.getKeystoreAlias(), sourceSpec.getPurposes());
- mUid = sourceSpec.getUid();
+ mNamespace = sourceSpec.getNamespace();
mKeySize = sourceSpec.getKeySize();
mSpec = sourceSpec.getAlgorithmParameterSpec();
mCertificateSubject = sourceSpec.getCertificateSubject();
@@ -873,12 +904,51 @@
*
* @param uid UID or {@code -1} for the UID of the current process.
*
+ * @deprecated Setting the UID of the target namespace is based on a hardcoded
+ * hack in the Keystore service. This is no longer supported with Keystore 2.0/Android S.
+ * Instead, dedicated non UID based namespaces can be configured in SEPolicy using
+ * the keystore2_key_contexts files. The functionality of this method will be supported
+ * by mapping knows special UIDs, such as WIFI, to the newly configured SELinux based
+ * namespaces. Unknown UIDs will yield {@link IllegalArgumentException}.
+ *
* @hide
*/
@SystemApi
@NonNull
+ @Deprecated
public Builder setUid(int uid) {
- mUid = uid;
+ if (!AndroidKeyStoreProvider.isKeystore2Enabled()) {
+ // If Keystore2 has not been enabled we have to behave as if mNamespace is actually
+ // a UID, because we are still being used with the old Keystore SPI.
+ // TODO This if statement and body can be removed when the Keystore 2 migration is
+ // complete. b/171563717
+ mNamespace = uid;
+ return this;
+ }
+ mNamespace = KeyProperties.legacyUidToNamespace(uid);
+ return this;
+ }
+
+ /**
+ * Set the designated SELinux namespace that the key shall live in. The caller must
+ * have sufficient permissions to install a key in the given namespace. Namespaces
+ * can be created using SEPolicy. The keystore2_key_contexts files map numeric
+ * namespaces to SELinux labels, and SEPolicy can be used to grant access to these
+ * namespaces to the desired target context. This is the preferred way to share
+ * keys between system and vendor components, e.g., WIFI settings and WPA supplicant.
+ *
+ * @param namespace Numeric SELinux namespace as configured in keystore2_key_contexts
+ * of Android's SEPolicy.
+ * TODO b/171806779 link to public Keystore 2.0 documentation.
+ * See bug for more details for now.
+ * @return this Builder object.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public Builder setNamespace(int namespace) {
+ mNamespace = namespace;
return this;
}
@@ -1489,7 +1559,7 @@
public KeyGenParameterSpec build() {
return new KeyGenParameterSpec(
mKeystoreAlias,
- mUid,
+ mNamespace,
mKeySize,
mSpec,
mCertificateSubject,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index d891a25..7158d0c 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -84,6 +84,7 @@
private final boolean mTrustedUserPresenceRequired;
private final boolean mInvalidatedByBiometricEnrollment;
private final boolean mUserConfirmationRequired;
+ private final @KeyProperties.SecurityLevelEnum int mSecurityLevel;
/**
* @hide
@@ -107,7 +108,8 @@
boolean userAuthenticationValidWhileOnBody,
boolean trustedUserPresenceRequired,
boolean invalidatedByBiometricEnrollment,
- boolean userConfirmationRequired) {
+ boolean userConfirmationRequired,
+ @KeyProperties.SecurityLevelEnum int securityLevel) {
mKeystoreAlias = keystoreKeyAlias;
mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
@@ -131,6 +133,7 @@
mTrustedUserPresenceRequired = trustedUserPresenceRequired;
mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
mUserConfirmationRequired = userConfirmationRequired;
+ mSecurityLevel = securityLevel;
}
/**
@@ -144,7 +147,10 @@
* Returns {@code true} if the key resides inside secure hardware (e.g., Trusted Execution
* Environment (TEE) or Secure Element (SE)). Key material of such keys is available in
* plaintext only inside the secure hardware and is not exposed outside of it.
+ *
+ * @deprecated This method is superseded by @see getSecurityLevel.
*/
+ @Deprecated
public boolean isInsideSecureHardware() {
return mInsideSecureHardware;
}
@@ -355,4 +361,17 @@
public boolean isTrustedUserPresenceRequired() {
return mTrustedUserPresenceRequired;
}
+
+ /**
+ * Returns the security level that the key is protected by.
+ * {@code KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT} and
+ * {@code KeyProperties.SecurityLevelEnum.STRONGBOX} indicate that the key material resides
+ * in secure hardware. Key material of such keys is available in
+ * plaintext only inside the secure hardware and is not exposed outside of it.
+ *
+ * <p>See {@link KeyProperties}.{@code SecurityLevelEnum} constants.
+ */
+ public @KeyProperties.SecurityLevelEnum int getSecurityLevel() {
+ return mSecurityLevel;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 9050c69..5928540 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.security.KeyStore;
import android.security.keymaster.KeymasterDefs;
import libcore.util.EmptyArray;
@@ -857,4 +858,43 @@
}
}
+ /**
+ * This value indicates the implicit keystore namespace of the calling application.
+ * It is used by default. Only select system components can choose a different namespace
+ * which it must be configured in SEPolicy.
+ * @hide
+ */
+ public static final int NAMESPACE_APPLICATION = -1;
+
+ /**
+ * For legacy support, translate namespaces into known UIDs.
+ * @hide
+ */
+ public static int namespaceToLegacyUid(int namespace) {
+ switch (namespace) {
+ case NAMESPACE_APPLICATION:
+ return KeyStore.UID_SELF;
+ // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
+ // b/171305388 and b/171305607
+ default:
+ throw new IllegalArgumentException("No UID corresponding to namespace "
+ + namespace);
+ }
+ }
+
+ /**
+ * For legacy support, translate namespaces into known UIDs.
+ * @hide
+ */
+ public static int legacyUidToNamespace(int uid) {
+ switch (uid) {
+ case KeyStore.UID_SELF:
+ return NAMESPACE_APPLICATION;
+ // TODO Translate WIFI and VPN UIDs once the namespaces are defined.
+ // b/171305388 and b/171305607
+ default:
+ throw new IllegalArgumentException("No namespace corresponding to uid "
+ + uid);
+ }
+ }
}