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

Commit 8b190a9d authored by Max Bires's avatar Max Bires Committed by Automerger Merge Worker
Browse files

Merge "Adding AIDL and functions for talking to RemoteProvisioner" am:...

Merge "Adding AIDL and functions for talking to RemoteProvisioner" am: e7f48b99 am: 2ace49a7 am: 6cd0a977

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1650991

Change-Id: I1e6c2d71e3ab7f9331df45d233e23619a50e5f38
parents 3878124c 6cd0a977
Loading
Loading
Loading
Loading
+99 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;

/**
 * GenerateKey is a helper class to handle interactions between Keystore and the RemoteProvisioner
 * app. There are two cases where Keystore should use this class.
 *
 * (1) : An app generates a new attested key pair, so Keystore calls notifyKeyGenerated to let the
 *       RemoteProvisioner app check if the state of the attestation key pool is getting low enough
 *       to warrant provisioning more attestation certificates early.
 *
 * (2) : An app attempts to generate a new key pair, but the keystore service discovers it is out of
 *       attestation key pairs and cannot provide one for the given application. Keystore can then
 *       make a blocking call on notifyEmpty to allow the RemoteProvisioner app to get another
 *       attestation certificate chain provisioned.
 *
 * In most cases, the proper usage of (1) should preclude the need for (2).
 *
 * @hide
 */
public class GenerateRkpKey {

    private IGenerateRkpKeyService mBinder;
    private Context mContext;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBinder = IGenerateRkpKeyService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            mBinder = null;
        }
    };

    /**
     * Constructor which takes a Context object.
     */
    public GenerateRkpKey(Context context) {
        mContext = context;
    }

    /**
     * Fulfills the use case of (2) described in the class documentation. Blocks until the
     * RemoteProvisioner application can get new attestation keys signed by the server.
     */
    public void notifyEmpty(int securityLevel) throws RemoteException {
        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
        intent.setComponent(comp);
        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
            throw new RemoteException("Failed to bind to GenerateKeyService");
        }
        if (mBinder != null) {
            mBinder.generateKey(securityLevel);
        }
        mContext.unbindService(mConnection);
    }

    /**
     * FUlfills the use case of (1) described in the class documentation. Non blocking call.
     */
    public void notifyKeyGenerated(int securityLevel) throws RemoteException {
        Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
        intent.setComponent(comp);
        if (comp == null || !mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
            throw new RemoteException("Failed to bind to GenerateKeyService");
        }
        if (mBinder != null) {
            mBinder.notifyKeyGenerated(securityLevel);
        }
        mContext.unbindService(mConnection);
    }
}
+31 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2021 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;

/**
 * Thrown on problems in attempting to attest to a key using a remotely provisioned key.
 *
 * @hide
 */
public class GenerateRkpKeyException extends Exception {

    /**
     * Constructs a new {@code GenerateRkpKeyException}.
     */
    public GenerateRkpKeyException() {
    }
}
+36 −0
Original line number Original line Diff line number Diff line
/**
 * Copyright (C) 2021 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;

/**
 * Interface to allow the framework to notify the RemoteProvisioner app when keys are empty. This
 * will be used if Keystore replies with an error code NO_KEYS_AVAILABLE in response to an
 * attestation request. The framework can then synchronously call generateKey() to get more
 * attestation keys generated and signed. Upon return, the caller can be certain an attestation key
 * is available.
 *
 * @hide
 */
interface IGenerateRkpKeyService {
    /**
     * Ping the provisioner service to let it know an app generated a key. This may or may not have
     * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
     */
    oneway void notifyKeyGenerated(in int securityLevel);
    /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
    void generateKey(in int securityLevel);
}
+33 −1
Original line number Original line Diff line number Diff line
@@ -24,6 +24,9 @@ import android.hardware.security.keymint.KeyPurpose;
import android.hardware.security.keymint.SecurityLevel;
import android.hardware.security.keymint.SecurityLevel;
import android.hardware.security.keymint.Tag;
import android.hardware.security.keymint.Tag;
import android.os.Build;
import android.os.Build;
import android.os.RemoteException;
import android.security.GenerateRkpKey;
import android.security.GenerateRkpKeyException;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore;
import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStore2;
@@ -520,6 +523,18 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato


    @Override
    @Override
    public KeyPair generateKeyPair() {
    public KeyPair generateKeyPair() {
        try {
            return generateKeyPairHelper();
        } catch (GenerateRkpKeyException e) {
            try {
                return generateKeyPairHelper();
            } catch (GenerateRkpKeyException f) {
                throw new ProviderException("Failed to provision new attestation keys.");
            }
        }
    }

    private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException {
        if (mKeyStore == null || mSpec == null) {
        if (mKeyStore == null || mSpec == null) {
            throw new IllegalStateException("Not initialized");
            throw new IllegalStateException("Not initialized");
        }
        }
@@ -557,13 +572,30 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
            AndroidKeyStorePublicKey publicKey =
            AndroidKeyStorePublicKey publicKey =
                    AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
                    AndroidKeyStoreProvider.makeAndroidKeyStorePublicKeyFromKeyEntryResponse(
                            descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);
                            descriptor, metadata, iSecurityLevel, mKeymasterAlgorithm);

            GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
            try {
                if (mSpec.getAttestationChallenge() != null) {
                    keyGen.notifyKeyGenerated(securityLevel);
                }
            } catch (RemoteException e) {
                // This is not really an error state, and necessarily does not apply to non RKP
                // systems or hybrid systems where RKP is not currently turned on.
                Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.");
            }
            success = true;
            success = true;
            return new KeyPair(publicKey, publicKey.getPrivateKey());
            return new KeyPair(publicKey, publicKey.getPrivateKey());
        } catch (android.security.KeyStoreException e) {
        } catch (android.security.KeyStoreException e) {
            switch(e.getErrorCode()) {
            switch(e.getErrorCode()) {
                case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
                case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
                    throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
                    throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
                case ResponseCode.OUT_OF_KEYS:
                    GenerateRkpKey keyGen = new GenerateRkpKey(KeyStore.getApplicationContext());
                    try {
                        keyGen.notifyEmpty(securityLevel);
                    } catch (RemoteException f) {
                        throw new ProviderException("Failed to talk to RemoteProvisioner", f);
                    }
                    throw new GenerateRkpKeyException();
                default:
                default:
                    ProviderException p = new ProviderException("Failed to generate key pair.", e);
                    ProviderException p = new ProviderException("Failed to generate key pair.", e);
                    if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
                    if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {