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