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

Commit 119c1d90 authored by Seth Moore's avatar Seth Moore Committed by Gerrit Code Review
Browse files

Merge "Implement IRegistration interface for RKP"

parents 91a15e8a 7ceb8aab
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -58,11 +58,4 @@ oneway interface IRemoteProvisioning {
     *
     */
    void getRegistration(String irpcName, IGetRegistrationCallback callback);

    /**
     * Cancel any active {@link getRegistration} call associated with the given
     * callback. If no getRegistration call is currently active, this function is
     * a noop.
     */
    void cancelGetRegistration(IGetRegistrationCallback callback);
}
+132 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 com.android.server.security.rkp;

import android.os.CancellationSignal;
import android.os.OperationCanceledException;
import android.os.OutcomeReceiver;
import android.security.rkp.IGetKeyCallback;
import android.security.rkp.IRegistration;
import android.security.rkp.service.RegistrationProxy;
import android.security.rkp.service.RemotelyProvisionedKey;
import android.util.Log;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;

/**
 * Implements android.security.rkp.IRegistration as a thin wrapper around the java code
 * exported by com.android.rkp.
 *
 * @hide
 */
final class RemoteProvisioningRegistration extends IRegistration.Stub {
    static final String TAG = RemoteProvisioningService.TAG;
    private final ConcurrentHashMap<IGetKeyCallback, CancellationSignal> mOperations =
            new ConcurrentHashMap<>();
    private final RegistrationProxy mRegistration;
    private final Executor mExecutor;

    private class GetKeyReceiver implements OutcomeReceiver<RemotelyProvisionedKey, Exception> {
        IGetKeyCallback mCallback;
        GetKeyReceiver(IGetKeyCallback callback) {
            mCallback = callback;
        }

        @Override
        public void onResult(RemotelyProvisionedKey result) {
            mOperations.remove(mCallback);
            Log.i(TAG, "Successfully fetched key for client " + mCallback.hashCode());
            android.security.rkp.RemotelyProvisionedKey parcelable =
                    new android.security.rkp.RemotelyProvisionedKey();
            parcelable.keyBlob = result.getKeyBlob();
            parcelable.encodedCertChain = result.getEncodedCertChain();
            wrapCallback(() -> mCallback.onSuccess(parcelable));
        }

        @Override
        public void onError(Exception e) {
            mOperations.remove(mCallback);
            if (e instanceof OperationCanceledException) {
                Log.i(TAG, "Operation cancelled for client " + mCallback.hashCode());
                wrapCallback(mCallback::onCancel);
            } else {
                Log.e(TAG, "Error fetching key for client " + mCallback.hashCode(), e);
                wrapCallback(() -> mCallback.onError(e.getMessage()));
            }
        }
    }

    RemoteProvisioningRegistration(RegistrationProxy registration, Executor executor) {
        mRegistration = registration;
        mExecutor = executor;
    }

    @Override
    public void getKey(int keyId, IGetKeyCallback callback) {
        CancellationSignal cancellationSignal = new CancellationSignal();
        if (mOperations.putIfAbsent(callback, cancellationSignal) != null) {
            Log.e(TAG, "Client can only request one call at a time " + callback.hashCode());
            throw new IllegalArgumentException(
                    "Callback is already associated with an existing operation: "
                            + callback.hashCode());
        }

        try {
            Log.i(TAG, "Fetching key " + keyId + " for client " + callback.hashCode());
            mRegistration.getKeyAsync(keyId, cancellationSignal, mExecutor,
                    new GetKeyReceiver(callback));
        } catch (Exception e) {
            Log.e(TAG, "getKeyAsync threw an exception for client " + callback.hashCode(), e);
            mOperations.remove(callback);
            wrapCallback(() -> callback.onError(e.getMessage()));
        }
    }

    @Override
    public void cancelGetKey(IGetKeyCallback callback) {
        CancellationSignal cancellationSignal = mOperations.remove(callback);
        if (cancellationSignal == null) {
            throw new IllegalArgumentException(
                    "Invalid client in cancelGetKey: " + callback.hashCode());
        }

        Log.i(TAG, "Requesting cancellation for client " + callback.hashCode());
        cancellationSignal.cancel();
    }

    @Override
    public void storeUpgradedKey(byte[] oldKeyBlob, byte[] newKeyBlob) {
        // TODO(b/262748535)
        Log.e(TAG, "RegistrationBinder.storeUpgradedKey NOT YET IMPLEMENTED");
    }

    interface CallbackRunner {
        void run() throws Exception;
    }

    private void wrapCallback(CallbackRunner callback) {
        // Exceptions resulting from notifications to IGetKeyCallback objects can only be logged,
        // since getKey execution is asynchronous, and there's no way for an exception to be
        // properly handled up the stack.
        try {
            callback.run();
        } catch (Exception e) {
            Log.e(TAG, "Error invoking callback on client binder", e);
        }
    }
}
+34 −59
Original line number Diff line number Diff line
@@ -20,9 +20,7 @@ import android.content.Context;
import android.os.Binder;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
import android.security.rkp.IGetKeyCallback;
import android.security.rkp.IGetRegistrationCallback;
import android.security.rkp.IRegistration;
import android.security.rkp.IRemoteProvisioning;
import android.security.rkp.service.RegistrationProxy;
import android.util.Log;
@@ -30,6 +28,7 @@ import android.util.Log;
import com.android.server.SystemService;

import java.time.Duration;
import java.util.concurrent.Executor;

/**
 * Implements the remote provisioning system service. This service is backed by a mainline
@@ -43,84 +42,60 @@ public class RemoteProvisioningService extends SystemService {
    private static final Duration CREATE_REGISTRATION_TIMEOUT = Duration.ofSeconds(10);
    private final RemoteProvisioningImpl mBinderImpl = new RemoteProvisioningImpl();

    /** @hide */
    public RemoteProvisioningService(Context context) {
        super(context);
    private static class RegistrationReceiver implements
            OutcomeReceiver<RegistrationProxy, Exception> {
        private final Executor mExecutor;
        private final IGetRegistrationCallback mCallback;

        RegistrationReceiver(Executor executor, IGetRegistrationCallback callback) {
            mExecutor = executor;
            mCallback = callback;
        }

        @Override
    public void onStart() {
        publishBinderService(Context.REMOTE_PROVISIONING_SERVICE, mBinderImpl);
        public void onResult(RegistrationProxy registration) {
            try {
                mCallback.onSuccess(new RemoteProvisioningRegistration(registration, mExecutor));
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling success callback " + mCallback.hashCode(), e);
            }

    private final class RemoteProvisioningImpl extends IRemoteProvisioning.Stub {

        final class RegistrationBinder extends IRegistration.Stub {
            static final String TAG = RemoteProvisioningService.TAG;
            private final RegistrationProxy mRegistration;

            RegistrationBinder(RegistrationProxy registration) {
                mRegistration = registration;
        }

        @Override
            public void getKey(int keyId, IGetKeyCallback callback) {
                Log.e(TAG, "RegistrationBinder.getKey NOT YET IMPLEMENTED");
        public void onError(Exception error) {
            try {
                mCallback.onError(error.toString());
            } catch (RemoteException e) {
                Log.e(TAG, "Error calling error callback " + mCallback.hashCode(), e);
            }
        }
    }

            @Override
            public void cancelGetKey(IGetKeyCallback callback) {
                Log.e(TAG, "RegistrationBinder.cancelGetKey NOT YET IMPLEMENTED");
    /** @hide */
    public RemoteProvisioningService(Context context) {
        super(context);
    }

    @Override
            public void storeUpgradedKey(byte[] oldKeyBlob, byte[] newKeyBlob) {
                Log.e(TAG, "RegistrationBinder.storeUpgradedKey NOT YET IMPLEMENTED");
            }
    public void onStart() {
        publishBinderService(Context.REMOTE_PROVISIONING_SERVICE, mBinderImpl);
    }

    private final class RemoteProvisioningImpl extends IRemoteProvisioning.Stub {
        @Override
        public void getRegistration(String irpcName, IGetRegistrationCallback callback)
                throws RemoteException {
            final int callerUid = Binder.getCallingUidOrThrow();
            final long callingIdentity = Binder.clearCallingIdentity();
            final Executor executor = getContext().getMainExecutor();
            try {
                Log.i(TAG, "getRegistration(" + irpcName + ")");
                RegistrationProxy.createAsync(
                        getContext(),
                        callerUid,
                        irpcName,
                        CREATE_REGISTRATION_TIMEOUT,
                        getContext().getMainExecutor(),
                        new OutcomeReceiver<>() {
                            @Override
                            public void onResult(RegistrationProxy registration) {
                                try {
                                    callback.onSuccess(new RegistrationBinder(registration));
                                } catch (RemoteException e) {
                                    Log.e(TAG, "Error calling success callback", e);
                                }
                            }

                            @Override
                            public void onError(Exception error) {
                                try {
                                    callback.onError(error.toString());
                                } catch (RemoteException e) {
                                    Log.e(TAG, "Error calling error callback", e);
                                }
                            }
                        });
                RegistrationProxy.createAsync(getContext(), callerUid, irpcName,
                        CREATE_REGISTRATION_TIMEOUT, executor,
                        new RegistrationReceiver(executor, callback));
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }
        }

        @Override
        public void cancelGetRegistration(IGetRegistrationCallback callback)
                throws RemoteException {
            Log.i(TAG, "cancelGetRegistration()");
            callback.onError("cancelGetRegistration not yet implemented");
        }
    }
}