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

Commit d00355cd authored by Pavel Grafov's avatar Pavel Grafov Committed by Android (Google) Code Review
Browse files

Merge "Keystore 2.0: Remove attestKey from KeyChain." into sc-dev

parents 5f630223 0f138dc0
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -37,8 +37,6 @@ interface IKeyChainService {
    void setUserSelectable(String alias, boolean isUserSelectable);

    int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
    int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
            out KeymasterCertificateChain chain);
    boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);

    // APIs used by CertInstaller and DevicePolicyManager
+44 −6
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import android.os.UserManager;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.util.Log;

import com.android.org.conscrypt.TrustedCertificateStore;
@@ -682,6 +684,33 @@ public final class KeyChain {
        return null;
    }

    /**
     * This prefix is used to disambiguate grant aliase strings from normal key alias strings.
     * Technically, a key alias string can use the same prefix. However, a collision does not
     * lead to privilege escalation, because grants are access controlled in the Keystore daemon.
     * @hide
     */
    public static final String GRANT_ALIAS_PREFIX = "ks2_keychain_grant_id:";

    private static KeyDescriptor getGrantDescriptor(String keyid) {
        KeyDescriptor result = new KeyDescriptor();
        result.domain = Domain.GRANT;
        result.blob = null;
        result.alias = null;
        try {
            result.nspace = Long.parseUnsignedLong(
                    keyid.substring(GRANT_ALIAS_PREFIX.length()), 16 /* radix */);
        } catch (NumberFormatException e) {
            return null;
        }
        return result;
    }

    /** @hide */
    public static String getGrantString(KeyDescriptor key) {
        return String.format(GRANT_ALIAS_PREFIX + "%016X", key.nspace);
    }

    /** @hide */
    @Nullable @WorkerThread
    public static KeyPair getKeyPair(@NonNull Context context, @NonNull String alias)
@@ -705,11 +734,23 @@ public final class KeyChain {

        if (keyId == null) {
            return null;
        }

        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
            try {
                return android.security.keystore2.AndroidKeyStoreProvider
                        .loadAndroidKeyStoreKeyPairFromKeystore(
                                KeyStore2.getInstance(),
                                getGrantDescriptor(keyId));
            } catch (UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
                throw new KeyChainException(e);
            }
        } else {
            try {
                return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyPairFromKeystore(
                        KeyStore.getInstance(), keyId, KeyStore.UID_SELF);
            } catch (RuntimeException | UnrecoverableKeyException | KeyPermanentlyInvalidatedException e) {
            } catch (RuntimeException | UnrecoverableKeyException
                    | KeyPermanentlyInvalidatedException e) {
                throw new KeyChainException(e);
            }
        }
@@ -827,11 +868,8 @@ public final class KeyChain {
    @Deprecated
    public static boolean isBoundKeyAlgorithm(
            @NonNull @KeyProperties.KeyAlgorithmEnum String algorithm) {
        if (!isKeyAlgorithmSupported(algorithm)) {
            return false;
        }

        return KeyStore.getInstance().isHardwareBacked(algorithm);
        // All supported algorithms are hardware backed. Individual keys may not be.
        return true;
    }

    /** @hide */
+16 −4
Original line number Diff line number Diff line
@@ -273,10 +273,10 @@ public class AndroidKeyStoreProvider extends Provider {
    /** @hide **/
    @NonNull
    public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
            @NonNull KeyStore2 keyStore, @NonNull String privateKeyAlias, int namespace)
            @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
        AndroidKeyStoreKey key =
                loadAndroidKeyStoreKeyFromKeystore(keyStore, privateKeyAlias, namespace);
                loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
        if (key instanceof AndroidKeyStorePublicKey) {
            AndroidKeyStorePublicKey publicKey = (AndroidKeyStorePublicKey) key;
            return new KeyPair(publicKey, publicKey.getPrivateKey());
@@ -348,6 +348,18 @@ public class AndroidKeyStoreProvider extends Provider {
        }
        descriptor.alias = alias;
        descriptor.blob = null;

        final AndroidKeyStoreKey key = loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
        if (key instanceof AndroidKeyStorePublicKey) {
            return ((AndroidKeyStorePublicKey) key).getPrivateKey();
        } else {
            return key;
        }
    }

    private static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
            @NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
            throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
        KeyEntryResponse response = null;
        try {
            response = keyStore.getKeyEntry(descriptor);
@@ -397,7 +409,7 @@ public class AndroidKeyStoreProvider extends Provider {
                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
            return makeAndroidKeyStorePublicKeyFromKeyEntryResponse(descriptor, response.metadata,
                    new KeyStoreSecurityLevel(response.iSecurityLevel),
                    keymasterAlgorithm).getPrivateKey();
                    keymasterAlgorithm);
        } else {
            throw new UnrecoverableKeyException("Key algorithm unknown");
        }
+43 −22
Original line number Diff line number Diff line
@@ -333,6 +333,7 @@ import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParserException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -340,6 +341,9 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
@@ -5622,7 +5626,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        // Get attestation flags, if any.
        final int[] attestationUtilsFlags = translateIdAttestationFlags(idAttestationFlags);
        final boolean deviceIdAttestationRequired = attestationUtilsFlags != null;
        final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
        KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
        final String alias = keySpec.getKeystoreAlias();
        Preconditions.checkStringNotEmpty(alias, "Empty alias provided");
@@ -5643,6 +5647,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                    || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp)));
        }
        if (TextUtils.isEmpty(alias)) {
            throw new IllegalArgumentException("Empty alias provided.");
        }
        // As the caller will be granted access to the key, ensure no UID was specified, as
        // it will not have the desired effect.
        if (keySpec.getUid() != KeyStore.UID_SELF) {
@@ -5651,19 +5658,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
            return false;
        }
        if (deviceIdAttestationRequired) {
            if (keySpec.getAttestationChallenge() == null) {
                throw new IllegalArgumentException(
                        "Requested Device ID attestation but challenge is empty.");
            }
            KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder(keySpec);
            specBuilder.setAttestationIds(attestationUtilsFlags);
            specBuilder.setDevicePropertiesAttestationIncluded(true);
            keySpec = specBuilder.build();
        }
        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
        final long id = mInjector.binderClearCallingIdentity();
        try {
            try (KeyChainConnection keyChainConnection =
                    KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
                    KeyChain.bindAsUser(mContext, userHandle)) {
                IKeyChainService keyChain = keyChainConnection.getService();
                // Copy the provided keySpec, excluding the attestation challenge, which will be
                // used later for requesting key attestation record.
                final KeyGenParameterSpec noAttestationSpec = new KeyGenParameterSpec.Builder(
                        keySpec).setAttestationChallenge(null).build();
                final int generationResult = keyChain.generateKeyPair(algorithm,
                    new ParcelableKeyGenParameterSpec(noAttestationSpec));
                        new ParcelableKeyGenParameterSpec(keySpec));
                if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
                    Log.e(LOG_TAG, String.format(
                            "KeyChain failed to generate a keypair, error %d.", generationResult));
@@ -5672,6 +5686,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                            throw new ServiceSpecificException(
                                    DevicePolicyManager.KEY_GEN_STRONGBOX_UNAVAILABLE,
                                    String.format("KeyChain error: %d", generationResult));
                        case KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS:
                            throw new UnsupportedOperationException(
                                "Device does not support Device ID attestation.");
                        default:
                            logGenerateKeyPairFailure(caller, isCredentialManagementApp);
                            return false;
@@ -5685,23 +5702,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
                // that UID.
                keyChain.setGrant(caller.getUid(), alias, true);
                final byte[] attestationChallenge = keySpec.getAttestationChallenge();
                if (attestationChallenge != null) {
                    final int attestationResult = keyChain.attestKey(
                            alias, attestationChallenge, attestationUtilsFlags, attestationChain);
                    if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
                        Log.e(LOG_TAG, String.format(
                                "Attestation for %s failed (rc=%d), deleting key.",
                                alias, attestationResult));
                        keyChain.removeKeyPair(alias);
                        if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
                            throw new UnsupportedOperationException(
                                    "Device does not support Device ID attestation.");
                try {
                    final List<byte[]> encodedCerts = new ArrayList();
                    final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
                    final byte[] certChainBytes = keyChain.getCaCertificates(alias);
                    encodedCerts.add(keyChain.getCertificate(alias));
                    if (certChainBytes != null) {
                        final Collection<X509Certificate> certs =
                                (Collection<X509Certificate>) certFactory.generateCertificates(
                                    new ByteArrayInputStream(certChainBytes));
                        for (X509Certificate cert : certs) {
                            encodedCerts.add(cert.getEncoded());
                        }
                    }
                    attestationChain.shallowCopyFrom(new KeymasterCertificateChain(encodedCerts));
                } catch (CertificateException e) {
                    logGenerateKeyPairFailure(caller, isCredentialManagementApp);
                    Log.e(LOG_TAG, "While retrieving certificate chain.", e);
                    return false;
                }
                }
                DevicePolicyEventLogger
                        .createEvent(DevicePolicyEnums.GENERATE_KEY_PAIR)
                        .setAdmin(caller.getPackageName())