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

Commit f50d4dc1 authored by Eran Messeri's avatar Eran Messeri Committed by Android (Google) Code Review
Browse files

Merge "DevicePolicyManager: Support attestation for generated keys."

parents eb8acafc a1730640
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -64,6 +64,9 @@ import android.security.AttestedKeyPair;
import android.security.Credentials;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.AttestationUtils;
import android.security.keystore.KeyAttestationException;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.service.restrictions.RestrictionsReceiver;
@@ -4005,15 +4008,27 @@ public class DevicePolicyManager {
        try {
            final ParcelableKeyGenParameterSpec parcelableSpec =
                    new ParcelableKeyGenParameterSpec(keySpec);
            KeymasterCertificateChain attestationChain = new KeymasterCertificateChain();
            final boolean success = mService.generateKeyPair(
                    admin, mContext.getPackageName(), algorithm, parcelableSpec);
                    admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain);
            if (!success) {
                Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
                return null;
            }

            final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
            return new AttestedKeyPair(keyPair, null);
            final String alias = keySpec.getKeystoreAlias();
            final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias);
            Certificate[] outputChain = null;
            try {
                if (AttestationUtils.isChainValid(attestationChain)) {
                    outputChain = AttestationUtils.parseCertificateChain(attestationChain);
                }
            } catch (KeyAttestationException e) {
                Log.e(TAG, "Error parsing attestation chain for alias " + alias, e);
                mService.removeKeyPair(admin, mContext.getPackageName(), alias);
                return null;
            }
            return new AttestedKeyPair(keyPair, outputChain);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (KeyChainException e) {
+4 −1
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.os.Bundle;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;

import java.util.List;
@@ -166,7 +167,9 @@ interface IDevicePolicyManager {
            in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
            boolean isUserSelectable);
    boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
            in ParcelableKeyGenParameterSpec keySpec,
            out KeymasterCertificateChain attestationChain);
    void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);

    void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package android.security;

import android.content.pm.StringParceledListSlice;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec;

/**
@@ -33,6 +34,7 @@ interface IKeyChainService {
    void setUserSelectable(String alias, boolean isUserSelectable);

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

    // APIs used by CertInstaller and DevicePolicyManager
    String installCaCertificate(in byte[] caCertificate);
+38 −15
Original line number Diff line number Diff line
@@ -72,6 +72,33 @@ public abstract class AttestationUtils {
     */
    public static final int ID_TYPE_MEID = 3;

    /**
     * Creates an array of X509Certificates from the provided KeymasterCertificateChain.
     *
     * @hide Only called by the DevicePolicyManager.
     */
    @NonNull public static X509Certificate[] parseCertificateChain(
            final KeymasterCertificateChain kmChain) throws
            KeyAttestationException {
        // Extract certificate chain.
        final Collection<byte[]> rawChain = kmChain.getCertificates();
        if (rawChain.size() < 2) {
            throw new KeyAttestationException("Attestation certificate chain contained "
                    + rawChain.size() + " entries. At least two are required.");
        }
        final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
        try {
            for (final byte[] cert : rawChain) {
                concatenatedRawChain.write(cert);
            }
            return CertificateFactory.getInstance("X.509").generateCertificates(
                    new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
                            .toArray(new X509Certificate[0]);
        } catch (Exception e) {
            throw new KeyAttestationException("Unable to construct certificate chain", e);
        }
    }

    /**
     * Performs attestation of the device's identifiers. This method returns a certificate chain
     * whose first element contains the requested device identifiers in an extension. The device's
@@ -173,22 +200,18 @@ public abstract class AttestationUtils {
                    KeyStore.getKeyStoreException(errorCode));
        }

        // Extract certificate chain.
        final Collection<byte[]> rawChain = outChain.getCertificates();
        if (rawChain.size() < 2) {
            throw new DeviceIdAttestationException("Attestation certificate chain contained "
                    + rawChain.size() + " entries. At least two are required.");
        }
        final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
        try {
            for (final byte[] cert : rawChain) {
                concatenatedRawChain.write(cert);
            return parseCertificateChain(outChain);
        } catch (KeyAttestationException e) {
            throw new DeviceIdAttestationException(e.getMessage(), e);
        }
            return CertificateFactory.getInstance("X.509").generateCertificates(
                    new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
                            .toArray(new X509Certificate[0]);
        } catch (Exception e) {
            throw new DeviceIdAttestationException("Unable to construct certificate chain", e);
    }

    /**
     * Returns true if the attestation chain provided is a valid key attestation chain.
     * @hide
     */
    public static boolean isChainValid(KeymasterCertificateChain chain) {
        return chain != null && chain.getCertificates().size() >= 2;
    }
}
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security.keystore;

/**
 * Thrown when {@link AttestationUtils} is unable to attest the given key or handle
 * the resulting attestation record.
 *
 * @hide
 */
public class KeyAttestationException extends Exception {
    /**
     * Constructs a new {@code KeyAttestationException} with the current stack trace and the
     * specified detail message.
     *
     * @param detailMessage the detail message for this exception.
     */
    public KeyAttestationException(String detailMessage) {
        super(detailMessage);
    }

    /**
     * Constructs a new {@code KeyAttestationException} with the current stack trace, the
     * specified detail message and the specified cause.
     *
     * @param message the detail message for this exception.
     * @param cause the cause of this exception, may be {@code null}.
     */
    public KeyAttestationException(String message, Throwable cause) {
        super(message, cause);
    }
}
Loading