diff options
-rw-r--r-- | api/current.txt | 3 | ||||
-rwxr-xr-x | api/system-current.txt | 7 | ||||
-rw-r--r-- | core/api/current.txt | 3 | ||||
-rw-r--r-- | core/api/system-current.txt | 7 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/AndroidKeyStoreProvider.java | 26 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java | 6 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/KeyGenParameterSpec.java | 88 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/KeyInfo.java | 21 | ||||
-rw-r--r-- | keystore/java/android/security/keystore/KeyProperties.java | 40 |
9 files changed, 186 insertions, 15 deletions
diff --git a/api/current.txt b/api/current.txt index 1a405d52c025..010c896235e3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -42821,10 +42821,11 @@ package android.security.keystore { 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 042e2dc53b9c..abe95aebf8b2 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9377,8 +9377,13 @@ package android.security.keystore { 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 35abacb4b451..75b34cd096e7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -40989,10 +40989,11 @@ package android.security.keystore { 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 f36f4f33d7fe..a795a53ac3b5 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8259,8 +8259,13 @@ package android.security.keystore { 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 71e6559c53c6..d1b4464c1aed 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java @@ -23,6 +23,7 @@ import android.security.KeyStore; 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 @@ public class AndroidKeyStoreProvider extends Provider { 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 @@ public class AndroidKeyStoreProvider extends Provider { // 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 970726051e11..3694d635422f 100644 --- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java @@ -211,7 +211,11 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { 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 688c4a7b5969..e9aac7ddb56d 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.BiometricManager; 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ public KeyGenParameterSpec( String keyStoreAlias, - int uid, + int namespace, int keySize, AlgorithmParameterSpec spec, X500Principal certificateSubject, @@ -337,7 +336,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } mKeystoreAlias = keyStoreAlias; - mUid = uid; + mNamespace = namespace; mKeySize = keySize; mSpec = spec; mCertificateSubject = certificateSubject; @@ -382,11 +381,43 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu */ 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 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * * @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 final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu 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 d891a25dba68..7158d0cf248e 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -84,6 +84,7 @@ public class KeyInfo implements KeySpec { private final boolean mTrustedUserPresenceRequired; private final boolean mInvalidatedByBiometricEnrollment; private final boolean mUserConfirmationRequired; + private final @KeyProperties.SecurityLevelEnum int mSecurityLevel; /** * @hide @@ -107,7 +108,8 @@ public class KeyInfo implements KeySpec { boolean userAuthenticationValidWhileOnBody, boolean trustedUserPresenceRequired, boolean invalidatedByBiometricEnrollment, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + @KeyProperties.SecurityLevelEnum int securityLevel) { mKeystoreAlias = keystoreKeyAlias; mInsideSecureHardware = insideSecureHardware; mOrigin = origin; @@ -131,6 +133,7 @@ public class KeyInfo implements KeySpec { mTrustedUserPresenceRequired = trustedUserPresenceRequired; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mUserConfirmationRequired = userConfirmationRequired; + mSecurityLevel = securityLevel; } /** @@ -144,7 +147,10 @@ public class KeyInfo implements KeySpec { * 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 class KeyInfo implements KeySpec { 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 9050c695eba7..5928540b19bf 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; 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 @@ public abstract class KeyProperties { } } + /** + * 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); + } + } } |