diff options
6 files changed, 258 insertions, 78 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 244df9b59cc8..de8e18046a39 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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"; diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 4d67d4623897..e7e2c619ee6a 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -403,6 +403,14 @@ public final class DeviceConfig { 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. * * @hide diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 5787f7c48138..8d5f553dba5c 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -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"); + RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider(); + if (rebootEscrowProvider == null) { + Slog.w(TAG, + "Had reboot escrow data for users, but RebootEscrowProvider 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]); - + 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; } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java new file mode 100644 index 000000000000..6c1040b596c8 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java @@ -0,0 +1,143 @@ +/* + * 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; + } +} diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java new file mode 100644 index 000000000000..857ad5fc312a --- /dev/null +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java @@ -0,0 +1,49 @@ +/* + * 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); +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index c4d121170624..98d64524d87a 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -95,13 +95,24 @@ public class RebootEscrowManagerTests { static class MockInjector extends RebootEscrowManager.Injector { private final IRebootEscrow mRebootEscrow; + private final RebootEscrowProviderInterface mRebootEscrowProvider; private final UserManager mUserManager; private final MockableRebootEscrowInjected mInjected; - MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow, + MockInjector(Context context, UserManager userManager, + IRebootEscrow rebootEscrow, MockableRebootEscrowInjected injected) { super(context); mRebootEscrow = rebootEscrow; + + RebootEscrowProviderHalImpl.Injector halInjector = + new RebootEscrowProviderHalImpl.Injector() { + @Override + public IRebootEscrow getRebootEscrow() { + return mRebootEscrow; + } + }; + mRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector); mUserManager = userManager; mInjected = injected; } @@ -112,8 +123,8 @@ public class RebootEscrowManagerTests { } @Override - public IRebootEscrow getRebootEscrow() { - return mRebootEscrow; + public RebootEscrowProviderInterface getRebootEscrowProvider() { + return mRebootEscrowProvider; } @Override |