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

Commit 6083210d authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge changes Icf10d577,Ia9d3cae7

* changes:
  Factor out a class to store the rebootEscrow key
  Add namespace in DeviceConfig to support ota teams's features
parents 6e35bf94 00872652
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7886,6 +7886,7 @@ package android.provider {
    field public static final String NAMESPACE_INTELLIGENCE_ATTENTION = "intelligence_attention";
    field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
    field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
    field public static final String NAMESPACE_OTA = "ota";
    field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
    field public static final String NAMESPACE_PERMISSIONS = "permissions";
    field public static final String NAMESPACE_PRIVACY = "privacy";
+8 −0
Original line number Diff line number Diff line
@@ -402,6 +402,14 @@ public final class DeviceConfig {
    @SystemApi
    public static final String NAMESPACE_PERMISSIONS = "permissions";

    /**
     * Namespace for ota related features.
     *
     * @hide
     */
    @SystemApi
    public static final String NAMESPACE_OTA = "ota";

    /**
     * Namespace for all widget related features.
     *
+43 −75
Original line number Diff line number Diff line
@@ -15,20 +15,15 @@
 */

package com.android.server.locksettings;

import static android.os.UserHandle.USER_SYSTEM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.Slog;

@@ -44,7 +39,6 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.NoSuchElementException;

class RebootEscrowManager {
    private static final String TAG = "RebootEscrowManager";
@@ -116,8 +110,24 @@ class RebootEscrowManager {
    static class Injector {
        protected Context mContext;

        private final RebootEscrowProviderInterface mRebootEscrowProvider;

        Injector(Context context) {
            mContext = context;
            RebootEscrowProviderInterface rebootEscrowProvider = null;
            // TODO(xunchang) add implementation for server based ror.
            if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
                    "server_based_ror_enabled", false)) {
                Slog.e(TAG, "Server based ror isn't implemented yet.");
            } else {
                rebootEscrowProvider = new RebootEscrowProviderHalImpl();
            }

            if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
                mRebootEscrowProvider = rebootEscrowProvider;
            } else {
                mRebootEscrowProvider = null;
            }
        }

        public Context getContext() {
@@ -128,15 +138,8 @@ class RebootEscrowManager {
            return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
        }

        @Nullable
        public IRebootEscrow getRebootEscrow() {
            try {
                return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
                        "android.hardware.rebootescrow.IRebootEscrow/default"));
            } catch (NoSuchElementException e) {
                Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
            }
            return null;
        public RebootEscrowProviderInterface getRebootEscrowProvider() {
            return mRebootEscrowProvider;
        }

        public int getBootCount() {
@@ -210,45 +213,18 @@ class RebootEscrowManager {
    }

    private RebootEscrowKey getAndClearRebootEscrowKey() {
        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
            return null;
        }

        try {
            byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
            if (escrowKeyBytes == null) {
                Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
                return null;
            } else if (escrowKeyBytes.length != 32) {
                Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
                        + escrowKeyBytes.length);
                return null;
            }

            // Make sure we didn't get the null key.
            int zero = 0;
            for (int i = 0; i < escrowKeyBytes.length; i++) {
                zero |= escrowKeyBytes[i];
            }
            if (zero == 0) {
                Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
        if (rebootEscrowProvider == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return null;
        }

            // Overwrite the existing key with the null key
            rebootEscrow.storeKey(new byte[32]);

        RebootEscrowKey key = rebootEscrowProvider.getAndClearRebootEscrowKey(null);
        if (key != null) {
            mEventLog.addEntry(RebootEscrowEvent.RETRIEVED_STORED_KEK);
            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not retrieve escrow data");
            return null;
        } catch (ServiceSpecificException e) {
            Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
            return null;
        }
        return key;
    }

    private boolean restoreRebootEscrowForUser(@UserIdInt int userId, RebootEscrowKey key) {
@@ -279,9 +255,9 @@ class RebootEscrowManager {
            return;
        }

        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            Slog.w(TAG, "Reboot escrow requested, but RebootEscrow HAL is unavailable");
        if (mInjector.getRebootEscrowProvider() == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return;
        }

@@ -293,6 +269,7 @@ class RebootEscrowManager {

        final RebootEscrowData escrowData;
        try {
            // TODO(xunchang) further wrap the escrowData with a key from keystore.
            escrowData = RebootEscrowData.fromSyntheticPassword(escrowKey, spVersion,
                    syntheticPassword);
        } catch (IOException e) {
@@ -330,18 +307,16 @@ class RebootEscrowManager {
        mRebootEscrowWanted = false;
        setRebootEscrowReady(false);

        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {

        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
        if (rebootEscrowProvider == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return;
        }

        mStorage.removeKey(REBOOT_ESCROW_ARMED_KEY, USER_SYSTEM);

        try {
            rebootEscrow.storeKey(new byte[32]);
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
        }
        rebootEscrowProvider.clearRebootEscrowKey();

        List<UserInfo> users = mUserManager.getUsers();
        for (UserInfo user : users) {
@@ -356,9 +331,10 @@ class RebootEscrowManager {
            return false;
        }

        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
        if (rebootEscrowProvider == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return false;
        }

@@ -372,15 +348,7 @@ class RebootEscrowManager {
            return false;
        }

        boolean armedRebootEscrow = false;
        try {
            rebootEscrow.storeKey(escrowKey.getKeyBytes());
            armedRebootEscrow = true;
            Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
        }

        boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, null);
        if (armedRebootEscrow) {
            mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
            mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
@@ -397,7 +365,7 @@ class RebootEscrowManager {
    }

    boolean prepareRebootEscrow() {
        if (mInjector.getRebootEscrow() == null) {
        if (mInjector.getRebootEscrowProvider() == null) {
            return false;
        }

@@ -408,7 +376,7 @@ class RebootEscrowManager {
    }

    boolean clearRebootEscrow() {
        if (mInjector.getRebootEscrow() == null) {
        if (mInjector.getRebootEscrowProvider() == null) {
            return false;
        }

+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.locksettings;

import android.annotation.Nullable;
import android.hardware.rebootescrow.IRebootEscrow;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.NoSuchElementException;

import javax.crypto.SecretKey;

/**
 * An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
 */
class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
    private static final String TAG = "RebootEscrowProvider";

    private final Injector mInjector;

    static class Injector {
        @Nullable
        public IRebootEscrow getRebootEscrow() {
            try {
                return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
                        "android.hardware.rebootescrow.IRebootEscrow/default"));
            } catch (NoSuchElementException e) {
                Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
            }
            return null;
        }
    }

    RebootEscrowProviderHalImpl() {
        mInjector = new Injector();
    }

    @VisibleForTesting
    RebootEscrowProviderHalImpl(Injector injector) {
        mInjector = injector;
    }

    @Override
    public boolean hasRebootEscrowSupport() {
        return mInjector.getRebootEscrow() != null;
    }

    @Override
    public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            Slog.w(TAG, "Had reboot escrow data for users, but RebootEscrow HAL is unavailable");
            return null;
        }

        try {
            byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
            if (escrowKeyBytes == null) {
                Slog.w(TAG, "Had reboot escrow data for users, but could not retrieve key");
                return null;
            } else if (escrowKeyBytes.length != 32) {
                Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
                        + escrowKeyBytes.length);
                return null;
            }

            // Make sure we didn't get the null key.
            int zero = 0;
            for (int i = 0; i < escrowKeyBytes.length; i++) {
                zero |= escrowKeyBytes[i];
            }
            if (zero == 0) {
                Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
                return null;
            }

            // Overwrite the existing key with the null key
            rebootEscrow.storeKey(new byte[32]);

            return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
        } catch (RemoteException e) {
            Slog.w(TAG, "Could not retrieve escrow data");
            return null;
        } catch (ServiceSpecificException e) {
            Slog.w(TAG, "Got service-specific exception: " + e.errorCode);
            return null;
        }
    }

    @Override
    public void clearRebootEscrowKey() {
        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            return;
        }

        try {
            rebootEscrow.storeKey(new byte[32]);
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
        }

    }

    @Override
    public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
        if (rebootEscrow == null) {
            Slog.w(TAG, "Escrow marked as ready, but RebootEscrow HAL is unavailable");
            return false;
        }

        try {
            // The HAL interface only accept 32 bytes data. And the encrypted bytes for the escrow
            // key may exceed that limit. So we just store the raw key bytes here.
            rebootEscrow.storeKey(escrowKey.getKeyBytes());
            Slog.i(TAG, "Reboot escrow key stored with RebootEscrow HAL");
            return true;
        } catch (RemoteException | ServiceSpecificException e) {
            Slog.e(TAG, "Failed escrow secret to RebootEscrow HAL", e);
        }
        return false;
    }
}
+49 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.locksettings;

import javax.crypto.SecretKey;

/**
 * Provides APIs for {@link RebootEscrowManager} to access and manage the reboot escrow key.
 * Implementations need to find a way to persist the key across a reboot, and securely discards the
 * persisted copy.
 *
 * @hide
 */
public interface RebootEscrowProviderInterface {
    /**
     * Returns true if the secure store/discard of reboot escrow key is supported.
     */
    boolean hasRebootEscrowSupport();

    /**
     * Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
     * use the input key to decrypt the RebootEscrowKey. Returns null on failure.
     */
    RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);

    /**
     * Clears the stored RebootEscrowKey.
     */
    void clearRebootEscrowKey();

    /**
     * Saves the given RebootEscrowKey, optionally encrypt the storage with the encryptionKey.
     */
    boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey);
}
Loading