1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.locksettings; 18 19 import static org.hamcrest.CoreMatchers.is; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotEquals; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertThat; 24 import static org.junit.Assert.assertTrue; 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.ArgumentMatchers.anyLong; 27 import static org.mockito.Mockito.doThrow; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.when; 30 31 import android.content.Context; 32 import android.content.ContextWrapper; 33 import android.os.RemoteException; 34 import android.platform.test.annotations.Presubmit; 35 36 import androidx.test.InstrumentationRegistry; 37 import androidx.test.filters.SmallTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import org.junit.Before; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 import org.mockito.stubbing.Answer; 44 45 import java.io.File; 46 47 import javax.crypto.SecretKey; 48 import javax.crypto.spec.SecretKeySpec; 49 50 @SmallTest 51 @Presubmit 52 @RunWith(AndroidJUnit4.class) 53 public class RebootEscrowProviderServerBasedImplTests { 54 private SecretKey mKeyStoreEncryptionKey; 55 private RebootEscrowKey mRebootEscrowKey; 56 private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection; 57 private LockSettingsStorageTestable mStorage; 58 private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider; 59 private Answer<byte[]> mFakeEncryption; 60 61 private static final byte[] TEST_AES_KEY = new byte[] { 62 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31, 63 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61, 64 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09, 65 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23, 66 }; 67 68 @Before setUp()69 public void setUp() throws Exception { 70 mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES"); 71 mRebootEscrowKey = RebootEscrowKey.generate(); 72 mServiceConnection = mock( 73 ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class); 74 75 Context context = new ContextWrapper(InstrumentationRegistry.getContext()); 76 mStorage = new LockSettingsStorageTestable(context, 77 new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings")); 78 mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage, 79 new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection)); 80 81 mFakeEncryption = invocation -> { 82 byte[] secret = invocation.getArgument(0); 83 for (int i = 0; i < secret.length; i++) { 84 secret[i] = (byte) (secret[i] ^ 0xf); 85 } 86 return secret; 87 }; 88 } 89 90 @Test getAndClearRebootEscrowKey_loopback_success()91 public void getAndClearRebootEscrowKey_loopback_success() throws Exception { 92 when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); 93 when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption); 94 95 assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); 96 mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); 97 assertTrue(mStorage.hasRebootEscrowServerBlob()); 98 99 100 RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( 101 mKeyStoreEncryptionKey); 102 assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes())); 103 assertFalse(mStorage.hasRebootEscrowServerBlob()); 104 } 105 106 @Test getAndClearRebootEscrowKey_WrongDecryptionMethod_failure()107 public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception { 108 when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); 109 when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer( 110 invocation -> { 111 byte[] secret = invocation.getArgument(0); 112 for (int i = 0; i < secret.length; i++) { 113 secret[i] = (byte) (secret[i] ^ 0xe); 114 } 115 return secret; 116 } 117 ); 118 119 assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); 120 mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); 121 assertTrue(mStorage.hasRebootEscrowServerBlob()); 122 123 // Expect to get wrong key bytes 124 RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( 125 mKeyStoreEncryptionKey); 126 assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes()); 127 assertFalse(mStorage.hasRebootEscrowServerBlob()); 128 } 129 130 @Test getAndClearRebootEscrowKey_ServiceConnectionException_failure()131 public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception { 132 when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption); 133 doThrow(RemoteException.class).when(mServiceConnection).unwrap(any(), anyLong()); 134 135 assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport()); 136 mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey); 137 assertTrue(mStorage.hasRebootEscrowServerBlob()); 138 139 // Expect to get null key bytes when the server service fails to unwrap the blob. 140 RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey( 141 mKeyStoreEncryptionKey); 142 assertNull(ks); 143 assertFalse(mStorage.hasRebootEscrowServerBlob()); 144 } 145 } 146