Merge changes I45054e68,I6f301314 am: 2befe56df0

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

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I4a083699efe686d26c6f3bc1f163f94504f1595f
This commit is contained in:
Tianjie Xu
2021-01-27 23:34:38 +00:00
committed by Automerger Merge Worker
6 changed files with 492 additions and 13 deletions

View File

@@ -88,6 +88,7 @@ class LockSettingsStorage {
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
@@ -317,6 +318,22 @@ class LockSettingsStorage {
deleteFile(getRebootEscrowFile(userId));
}
public void writeRebootEscrowServerBlob(byte[] serverBlob) {
writeFile(getRebootEscrowServerBlob(), serverBlob);
}
public byte[] readRebootEscrowServerBlob() {
return readFile(getRebootEscrowServerBlob());
}
public boolean hasRebootEscrowServerBlob() {
return hasFile(getRebootEscrowServerBlob());
}
public void removeRebootEscrowServerBlob() {
deleteFile(getRebootEscrowServerBlob());
}
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
@@ -445,6 +462,12 @@ class LockSettingsStorage {
return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
}
@VisibleForTesting
String getRebootEscrowServerBlob() {
// There is a single copy of server blob for all users.
return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
}
private String getLockCredentialFilePathForUser(int userId, String basename) {
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
SYSTEM_DIRECTORY;

View File

@@ -124,26 +124,28 @@ class RebootEscrowManager {
static class Injector {
protected Context mContext;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final LockSettingsStorage mStorage;
private RebootEscrowProviderInterface mRebootEscrowProvider;
Injector(Context context) {
Injector(Context context, LockSettingsStorage storage) {
mContext = context;
mStorage = storage;
mKeyStoreManager = new RebootEscrowKeyStoreManager();
}
RebootEscrowProviderInterface rebootEscrowProvider = null;
// TODO(xunchang) add implementation for server based ror.
private RebootEscrowProviderInterface createRebootEscrowProvider() {
RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
Slog.e(TAG, "Server based ror isn't implemented yet.");
rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
}
if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
mRebootEscrowProvider = rebootEscrowProvider;
} else {
mRebootEscrowProvider = null;
if (rebootEscrowProvider.hasRebootEscrowSupport()) {
return rebootEscrowProvider;
}
return null;
}
public Context getContext() {
@@ -159,6 +161,12 @@ class RebootEscrowManager {
}
public RebootEscrowProviderInterface getRebootEscrowProvider() {
// Initialize for the provider lazily. Because the device_config and service
// implementation apps may change when system server is running.
if (mRebootEscrowProvider == null) {
mRebootEscrowProvider = createRebootEscrowProvider();
}
return mRebootEscrowProvider;
}
@@ -177,7 +185,7 @@ class RebootEscrowManager {
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
this(new Injector(context), callbacks, storage);
this(new Injector(context, storage), callbacks, storage);
}
@VisibleForTesting

View File

@@ -0,0 +1,202 @@
/*
* 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 com.android.server.locksettings;
import android.annotation.Nullable;
import android.content.Context;
import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import javax.crypto.SecretKey;
/**
* An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
* encrypt & decrypt the blob.
*/
class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
private static final String TAG = "RebootEscrowProvider";
// Timeout for service binding
private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
/**
* Use the default lifetime of 10 minutes. The lifetime covers the following activities:
* Server wrap secret -> device reboot -> server unwrap blob.
*/
private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
private final LockSettingsStorage mStorage;
private final Injector mInjector;
static class Injector {
private ResumeOnRebootServiceConnection mServiceConnection = null;
Injector(Context context) {
mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
if (mServiceConnection == null) {
Slog.e(TAG, "Failed to resolve resume on reboot server service.");
}
}
Injector(ResumeOnRebootServiceConnection serviceConnection) {
mServiceConnection = serviceConnection;
}
@Nullable
private ResumeOnRebootServiceConnection getServiceConnection() {
return mServiceConnection;
}
long getServiceTimeoutInSeconds() {
return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
"server_based_service_timeout_in_seconds",
DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
}
long getServerBlobLifetimeInMillis() {
return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
"server_based_server_blob_lifetime_in_millis",
DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
}
}
RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
this(storage, new Injector(context));
}
@VisibleForTesting
RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
mStorage = storage;
mInjector = injector;
}
@Override
public boolean hasRebootEscrowSupport() {
return mInjector.getServiceConnection() != null;
}
private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
TimeoutException, RemoteException, IOException {
ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
if (serviceConnection == null) {
Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+ " service is unavailable");
return null;
}
// Decrypt with k_k from the key store first.
byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
if (decryptedBlob == null) {
Slog.w(TAG, "Decrypted server blob should not be null");
return null;
}
// Ask the server connection service to decrypt the inner layer, to get the reboot
// escrow key (k_s).
serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
mInjector.getServiceTimeoutInSeconds());
serviceConnection.unbindService();
return escrowKeyBytes;
}
@Override
public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
// Delete the server blob in storage.
mStorage.removeRebootEscrowServerBlob();
if (serverBlob == null) {
Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
return null;
}
try {
byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
if (escrowKeyBytes == null) {
Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
return null;
} else if (escrowKeyBytes.length != 32) {
Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+ escrowKeyBytes.length);
return null;
}
return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
} catch (TimeoutException | RemoteException | IOException e) {
Slog.w(TAG, "Failed to decrypt the server blob ", e);
return null;
}
}
@Override
public void clearRebootEscrowKey() {
mStorage.removeRebootEscrowServerBlob();
}
private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
TimeoutException, RemoteException, IOException {
ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
if (serviceConnection == null) {
Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+ " service is unavailable");
return null;
}
serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
// Ask the server connection service to encrypt the reboot escrow key.
byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
serviceConnection.unbindService();
if (serverEncryptedBlob == null) {
Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
return null;
}
// Additionally wrap the server blob with a local key.
return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
}
@Override
public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
mStorage.removeRebootEscrowServerBlob();
try {
byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
if (wrappedBlob == null) {
Slog.w(TAG, "Failed to encrypt the reboot escrow key");
return false;
}
mStorage.writeRebootEscrowServerBlob(wrappedBlob);
Slog.i(TAG, "Reboot escrow key encrypted and stored.");
return true;
} catch (TimeoutException | RemoteException | IOException e) {
Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
}
return false;
}
}

View File

@@ -81,6 +81,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage {
super.getChildProfileLockFile(userId)).getAbsolutePath();
}
@Override
String getRebootEscrowServerBlob() {
return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
}
@Override
protected File getSyntheticPasswordDirectoryForUser(int userId) {
return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(

View File

@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.RebootEscrowListener;
import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +94,7 @@ public class RebootEscrowManagerTests {
private UserManager mUserManager;
private RebootEscrowManager.Callbacks mCallbacks;
private IRebootEscrow mRebootEscrow;
private ResumeOnRebootServiceConnection mServiceConnection;
private RebootEscrowKeyStoreManager mKeyStoreManager;
LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@ public class RebootEscrowManagerTests {
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@ public class RebootEscrowManagerTests {
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
RebootEscrowKeyStoreManager keyStoreManager,
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
super(context);
super(context, storage);
mRebootEscrow = rebootEscrow;
mServiceConnection = null;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -133,6 +138,22 @@ public class RebootEscrowManagerTests {
mInjected = injected;
}
MockInjector(Context context, UserManager userManager,
ResumeOnRebootServiceConnection serviceConnection,
RebootEscrowKeyStoreManager keyStoreManager,
LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
super(context, storage);
mServiceConnection = serviceConnection;
mRebootEscrow = null;
RebootEscrowProviderServerBasedImpl.Injector injector =
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
mUserManager = userManager;
mKeyStoreManager = keyStoreManager;
mInjected = injected;
}
@Override
public UserManager getUserManager() {
return mUserManager;
@@ -165,6 +186,7 @@ public class RebootEscrowManagerTests {
mUserManager = mock(UserManager.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
@@ -186,7 +208,12 @@ public class RebootEscrowManagerTests {
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
mKeyStoreManager, mInjected), mCallbacks, mStorage);
mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
}
private void setServerBasedRebootEscrowProvider() throws Exception {
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
}
@Test
@@ -201,6 +228,19 @@ public class RebootEscrowManagerTests {
verify(mRebootEscrow, never()).storeKey(any());
}
@Test
public void prepareRebootEscrowServerBased_Success() throws Exception {
setServerBasedRebootEscrowProvider();
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
mService.prepareRebootEscrow();
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
verify(mockListener).onPreparedForReboot(eq(true));
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
assertFalse(mStorage.hasRebootEscrowServerBlob());
}
@Test
public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
@@ -245,6 +285,28 @@ public class RebootEscrowManagerTests {
assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
}
@Test
public void armServiceServerBased_Success() throws Exception {
setServerBasedRebootEscrowProvider();
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
mService.prepareRebootEscrow();
clearInvocations(mServiceConnection);
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
verify(mockListener).onPreparedForReboot(eq(true));
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
assertTrue(mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
assertTrue(mStorage.hasRebootEscrowServerBlob());
}
@Test
public void armService_HalFailure_NonFatal() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
@@ -345,6 +407,40 @@ public class RebootEscrowManagerTests {
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
}
@Test
public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
setServerBasedRebootEscrowProvider();
when(mInjected.getBootCount()).thenReturn(0);
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
mService.prepareRebootEscrow();
clearInvocations(mServiceConnection);
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
verify(mockListener).onPreparedForReboot(eq(true));
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
// Use x -> x for both wrap & unwrap functions.
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
assertTrue(mService.armRebootEscrowIfNeeded());
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
assertTrue(mStorage.hasRebootEscrowServerBlob());
// pretend reboot happens here
when(mInjected.getBootCount()).thenReturn(1);
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
when(mServiceConnection.unwrap(any(), anyLong()))
.thenAnswer(invocation -> invocation.getArgument(0));
mService.loadRebootEscrowDataIfAvailable();
verify(mServiceConnection).unwrap(any(), anyLong());
assertTrue(metricsSuccessCaptor.getValue());
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
}
@Test
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
when(mInjected.getBootCount()).thenReturn(0);

View File

@@ -0,0 +1,145 @@
/*
* 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 com.android.server.locksettings;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContextWrapper;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.stubbing.Answer;
import java.io.File;
import java.io.IOException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
public class RebootEscrowProviderServerBasedImplTests {
private SecretKey mKeyStoreEncryptionKey;
private RebootEscrowKey mRebootEscrowKey;
private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
private LockSettingsStorageTestable mStorage;
private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
private Answer<byte[]> mFakeEncryption;
private static final byte[] TEST_AES_KEY = new byte[] {
0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
};
@Before
public void setUp() throws Exception {
mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
mRebootEscrowKey = RebootEscrowKey.generate();
mServiceConnection = mock(
ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
Context context = new ContextWrapper(InstrumentationRegistry.getContext());
mStorage = new LockSettingsStorageTestable(context,
new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
mFakeEncryption = invocation -> {
byte[] secret = invocation.getArgument(0);
for (int i = 0; i < secret.length; i++) {
secret[i] = (byte) (secret[i] ^ 0xf);
}
return secret;
};
}
@Test
public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
assertTrue(mStorage.hasRebootEscrowServerBlob());
RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
mKeyStoreEncryptionKey);
assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
assertFalse(mStorage.hasRebootEscrowServerBlob());
}
@Test
public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
invocation -> {
byte[] secret = invocation.getArgument(0);
for (int i = 0; i < secret.length; i++) {
secret[i] = (byte) (secret[i] ^ 0xe);
}
return secret;
}
);
assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
assertTrue(mStorage.hasRebootEscrowServerBlob());
// Expect to get wrong key bytes
RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
mKeyStoreEncryptionKey);
assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
assertFalse(mStorage.hasRebootEscrowServerBlob());
}
@Test
public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
assertTrue(mStorage.hasRebootEscrowServerBlob());
// Expect to get null key bytes when the server service fails to unwrap the blob.
RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
mKeyStoreEncryptionKey);
assertNull(ks);
assertFalse(mStorage.hasRebootEscrowServerBlob());
}
}