Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a05bd65c authored by Janis Danisevskis's avatar Janis Danisevskis
Browse files

Keystore 2.0: Update credential settings to use public Keystore API.

Test: N/A
Bug: 171305607
Bug: 171305388
Change-Id: I377115aca6b2df8052ae118f986c2f713535b6ec
parent f69f3ed7
Loading
Loading
Loading
Loading
+95 −59
Original line number Diff line number Diff line
@@ -34,9 +34,9 @@ import android.security.Credentials;
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;
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 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment
            }

            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);
                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);
                    }
                if (storedTypes.contains(Credential.Type.CA_CERTIFICATE)) {
                    keyStore.delete(Credentials.CA_CERTIFICATE + credential.getAlias(),
                            Process.WIFI_UID);
                    keyStore.deleteEntry(credential.getAlias());
                } catch (Exception e) {
                    throw new RuntimeException("Failed to delete keys from keystore.");
                }
            }

@@ -266,73 +272,103 @@ public class UserCredentialsSettings extends SettingsPreferenceFragment
         */
        @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 credentials;
            } catch (Exception e) {
                throw new RuntimeException("Failed to load credentials from Keystore.", e);
            }
                return keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
                        keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC;
        }

        private SortedMap<String, Credential> getCredentialsForUid(KeyStore keyStore, int uid) {
            try {
                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) {
                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;
                            }
                        } catch (UnrecoverableKeyException e) {
                            Log.e(TAG, "Unable to determine algorithm of key: " + prefix + alias, e);
                            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);
                            }
                        Credential c = aliasMap.get(alias);
                        if (c == null) {
                            c = new Credential(alias, uid);
                            aliasMap.put(alias, c);
                        }
                        c.storedTypes.add(type);
                    } 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);
                        }
                    }
                    aliasMap.put(alias, c);
                }
                return aliasMap;
            } catch (KeyStoreException e) {
                throw new RuntimeException("Failed to load credential from Android Keystore.", e);
            }
        }

        @Override
+0 −1
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ public final class CredentialStorage extends FragmentActivity {

    private static final int CONFIRM_CLEAR_SYSTEM_CREDENTIAL_REQUEST = 1;

    private final KeyStore mKeyStore = KeyStore.getInstance();
    private LockPatternUtils mUtils;

    /**
+19 −3
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.settings.security;

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.Lifecycle;
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 class ResetCredentialsPreferenceController extends RestrictedEncryptionPr

    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 @@ public class ResetCredentialsPreferenceController extends RestrictedEncryptionPr
    @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);
        }
    }
}