Merge "Keystore 2.0: Update credential settings to use public Keystore API." am: 7086917968

Original change: https://android-review.googlesource.com/c/platform/packages/apps/Settings/+/1569700

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I31b4a6e88530092dc2849dc4fb7e09c0d5463db3
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index d322819..5f72ca5 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -34,9 +34,9 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.security.KeyStore;
-import android.security.keymaster.KeyCharacteristics;
-import android.security.keymaster.KeymasterDefs;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyProperties;
+import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
@@ -55,13 +55,21 @@
 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 import com.android.settingslib.RestrictedLockUtilsInternal;
 
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
 import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.EnumSet;
+import java.util.Enumeration;
 import java.util.List;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
+import javax.crypto.SecretKey;
+
 public class UserCredentialsSettings extends SettingsPreferenceFragment
         implements View.OnClickListener {
     private static final String TAG = "UserCredentialsSettings";
@@ -201,21 +209,19 @@
             }
 
             private void deleteWifiCredential(final Credential credential) {
-                final KeyStore keyStore = KeyStore.getInstance();
-                final EnumSet<Credential.Type> storedTypes = credential.getStoredTypes();
-
-                // Remove all Wi-Fi credentials
-                if (storedTypes.contains(Credential.Type.USER_KEY)) {
-                    keyStore.delete(Credentials.USER_PRIVATE_KEY + credential.getAlias(),
-                            Process.WIFI_UID);
-                }
-                if (storedTypes.contains(Credential.Type.USER_CERTIFICATE)) {
-                    keyStore.delete(Credentials.USER_CERTIFICATE + credential.getAlias(),
-                            Process.WIFI_UID);
-                }
-                if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) {
-                    keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(),
-                            Process.WIFI_UID);
+                try {
+                    KeyStore keyStore = null;
+                    if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+                        keyStore = KeyStore.getInstance("AndroidKeyStore");
+                        keyStore.load(
+                                new AndroidKeyStoreLoadStoreParameter(
+                                        KeyProperties.NAMESPACE_WIFI));
+                    } else {
+                        keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
+                    }
+                    keyStore.deleteEntry(credential.getAlias());
+                } catch (Exception e) {
+                    throw new RuntimeException("Failed to delete keys from keystore.");
                 }
             }
 
@@ -266,73 +272,103 @@
          */
         @Override
         protected List<Credential> doInBackground(Void... params) {
-            final KeyStore keyStore = KeyStore.getInstance();
-
             // Certificates can be installed into SYSTEM_UID or WIFI_UID through CertInstaller.
             final int myUserId = UserHandle.myUserId();
             final int systemUid = UserHandle.getUid(myUserId, Process.SYSTEM_UID);
             final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
 
-            List<Credential> credentials = new ArrayList<>();
-            credentials.addAll(getCredentialsForUid(keyStore, systemUid).values());
-            credentials.addAll(getCredentialsForUid(keyStore, wifiUid).values());
-            return credentials;
-        }
+            try {
+                KeyStore processKeystore = KeyStore.getInstance("AndroidKeyStore");
+                processKeystore.load(null);
+                KeyStore wifiKeystore = null;
+                if (myUserId == 0) {
+                    // Only the primary user may see wifi configurations.
+                    if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+                        wifiKeystore = KeyStore.getInstance("AndroidKeyStore");
+                        wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
+                                KeyProperties.NAMESPACE_WIFI));
+                    } else {
+                        wifiKeystore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
+                    }
+                }
 
-        private boolean isAsymmetric(KeyStore keyStore, String alias, int uid)
-            throws UnrecoverableKeyException {
-                KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
-                int errorCode = keyStore.getKeyCharacteristics(alias, null, null, uid,
-                        keyCharacteristics);
-                if (errorCode != KeyStore.NO_ERROR) {
-                    throw (UnrecoverableKeyException)
-                            new UnrecoverableKeyException("Failed to obtain information about key")
-                                    .initCause(KeyStore.getKeyStoreException(errorCode));
+                List<Credential> credentials = new ArrayList<>();
+                credentials.addAll(getCredentialsForUid(processKeystore, systemUid).values());
+                if (wifiKeystore != null) {
+                    credentials.addAll(getCredentialsForUid(wifiKeystore, wifiUid).values());
                 }
-                Integer keymasterAlgorithm = keyCharacteristics.getEnum(
-                        KeymasterDefs.KM_TAG_ALGORITHM);
-                if (keymasterAlgorithm == null) {
-                    throw new UnrecoverableKeyException("Key algorithm unknown");
-                }
-                return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
-                        keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
+                return credentials;
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to load credentials from Keystore.", e);
+            }
         }
 
         private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
-            final SortedMap<String, Credential> aliasMap = new TreeMap<>();
-            for (final Credential.Type type : Credential.Type.values()) {
-                for (final String prefix : type.prefix) {
-                    for (final String alias : keyStore.list(prefix, uid)) {
-                        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID) {
+            try {
+                final SortedMap<String, Credential> aliasMap = new TreeMap<>();
+                boolean isSystem = UserHandle.getAppId(uid) == Process.SYSTEM_UID;
+                Enumeration<String> aliases = keyStore.aliases();
+                while (aliases.hasMoreElements()) {
+                    String alias = aliases.nextElement();
+                    Credential c = new Credential(alias, uid);
+                    Key key = null;
+                    try {
+                        key = keyStore.getKey(alias, null);
+                    } catch (NoSuchAlgorithmException | UnrecoverableKeyException e) {
+                        Log.e(TAG, "Error tying to retrieve key: " + alias, e);
+                        continue;
+                    }
+                    if (key != null) {
+                        // So we have a key
+                        if (key instanceof SecretKey) {
+                            // We don't display any symmetric key entries.
+                            continue;
+                        }
+                        if (isSystem) {
                             // Do not show work profile keys in user credentials
                             if (alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT) ||
                                     alias.startsWith(LockPatternUtils.PROFILE_KEY_NAME_DECRYPT)) {
                                 continue;
                             }
                             // Do not show synthetic password keys in user credential
+                            // We should never reach this point because the synthetic password key
+                            // is symmetric.
                             if (alias.startsWith(LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX)) {
                                 continue;
                             }
                         }
-                        try {
-                            if (type == Credential.Type.USER_KEY &&
-                                    !isAsymmetric(keyStore, prefix + alias, uid)) {
-                                continue;
+                        // At this point we have determined that we have an asymmetric key.
+                        // so we have at least a USER_KEY and USER_CERTIFICATE.
+                        c.storedTypes.add(Credential.Type.USER_KEY);
+
+                        Certificate[] certs =  keyStore.getCertificateChain(alias);
+                        if (certs != null) {
+                            c.storedTypes.add(Credential.Type.USER_CERTIFICATE);
+                            if (certs.length > 1) {
+                                c.storedTypes.add(Credential.Type.CA_CERTIFICATE);
                             }
-                        } catch (UnrecoverableKeyException e) {
-                            Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
-                            continue;
                         }
-                        Credential c = aliasMap.get(alias);
-                        if (c == null) {
-                            c = new Credential(alias, uid);
-                            aliasMap.put(alias, c);
+                    } else {
+                        // So there is no key but we have an alias. This must mean that we have
+                        // some certificate.
+                        if (keyStore.isCertificateEntry(alias)) {
+                            c.storedTypes.add(Credential.Type.CA_CERTIFICATE);
+                        } else {
+                            // This is a weired inconsistent case that should not exist.
+                            // Pure trusted certificate entries should be stored in CA_CERTIFICATE,
+                            // but if isCErtificateEntry returns null this means that only the
+                            // USER_CERTIFICATE is populated which should never be the case without
+                            // a private key. It can still be retrieved with
+                            // keystore.getCertificate().
+                            c.storedTypes.add(Credential.Type.USER_CERTIFICATE);
                         }
-                        c.storedTypes.add(type);
                     }
+                    aliasMap.put(alias, c);
                 }
+                return aliasMap;
+            } catch (KeyStoreException e) {
+                throw new RuntimeException("Failed to load credential from Android Keystore.", e);
             }
-            return aliasMap;
         }
 
         @Override
diff --git a/src/com/android/settings/security/CredentialStorage.java b/src/com/android/settings/security/CredentialStorage.java
index 5e64723..7b9f419 100644
--- a/src/com/android/settings/security/CredentialStorage.java
+++ b/src/com/android/settings/security/CredentialStorage.java
@@ -63,7 +63,6 @@
 
     private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;
 
-    private final KeyStore mKeyStore = KeyStore.getInstance();
     private LockPatternUtils mUtils;
 
     /**
diff --git a/src/com/android/settings/security/ResetCredentialsPreferenceController.java b/src/com/android/settings/security/ResetCredentialsPreferenceController.java
index 0700b46..48e7f84 100644
--- a/src/com/android/settings/security/ResetCredentialsPreferenceController.java
+++ b/src/com/android/settings/security/ResetCredentialsPreferenceController.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.UserManager;
-import android.security.KeyStore;
 
 import androidx.preference.PreferenceScreen;
 
@@ -27,6 +26,9 @@
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnResume;
 
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+
 public class ResetCredentialsPreferenceController extends RestrictedEncryptionPreferenceController
         implements LifecycleObserver, OnResume {
 
@@ -38,7 +40,13 @@
 
     public ResetCredentialsPreferenceController(Context context, Lifecycle lifecycle) {
         super(context, UserManager.DISALLOW_CONFIG_CREDENTIALS);
-        mKeyStore = KeyStore.getInstance();
+        KeyStore keyStore = null;
+        try {
+            keyStore = KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+        } catch (Exception e) {
+        }
+        mKeyStore = keyStore;
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
@@ -58,7 +66,15 @@
     @Override
     public void onResume() {
         if (mPreference != null && !mPreference.isDisabledByAdmin()) {
-            mPreference.setEnabled(!mKeyStore.isEmpty());
+            boolean isEnabled = false;
+            try {
+                if (mKeyStore != null) {
+                    isEnabled = mKeyStore.aliases().hasMoreElements();
+                }
+            } catch (KeyStoreException e) {
+                // If access to keystore fails, treat as disabled.
+            }
+            mPreference.setEnabled(isEnabled);
         }
     }
 }