blob: a7ed454b075c6b303c61f759f394ee522bba2fa5 [file] [log] [blame]
/*
* Copyright (C) 2019 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.car.trust;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.AdvertiseCallback;
import android.car.encryptionrunner.DummyEncryptionRunner;
import android.car.encryptionrunner.EncryptionRunnerFactory;
import android.car.encryptionrunner.HandshakeMessage;
import android.car.trust.TrustedDeviceInfo;
import android.car.userlib.CarUserManagerHelper;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.RemoteException;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.car.Utils;
import com.android.car.trust.CarTrustAgentBleManager.SendMessageCallback;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.UUID;
/**
* Unit test for {@link CarTrustAgentEnrollmentService} and {@link CarTrustedDeviceService}.
*/
@RunWith(AndroidJUnit4.class)
public class CarTrustAgentEnrollmentServiceTest {
private static final long TEST_HANDLE1 = 1L;
private static final long TEST_HANDLE2 = 2L;
private static final String DEVICE_ID = "device_id";
private static final String KEY = "key";
// Random uuid for test
private static final UUID TEST_ID1 = UUID.fromString("9a138a69-7c29-400f-9e71-fc29516f9f8b");
private static final UUID TEST_ID2 = UUID.fromString("3e344860-e688-4cce-8411-16161b61ad57");
private static final String TEST_TOKEN = "test_escrow_token";
private static final String ADDRESS = "00:11:22:33:AA:BB";
private static final String DEFAULT_NAME = "My Device";
private static final TrustedDeviceInfo DEVICE_INFO1 = new TrustedDeviceInfo(
TEST_HANDLE1, ADDRESS, DEFAULT_NAME);
private static final TrustedDeviceInfo DEVICE_INFO2 = new TrustedDeviceInfo(
TEST_HANDLE2, ADDRESS, DEFAULT_NAME);
private Context mContext;
private CarTrustedDeviceService mCarTrustedDeviceService;
private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
private BluetoothDevice mBluetoothDevice;
private int mUserId;
@Mock
private CarTrustAgentBleManager mMockCarTrustAgentBleManager;
@Mock
private CarTrustAgentEnrollmentService.CarTrustAgentEnrollmentRequestDelegate mEnrollDelegate =
new CarTrustAgentEnrollmentService.CarTrustAgentEnrollmentRequestDelegate() {
@Override
public void addEscrowToken(byte[] token, int uid) {
}
@Override
public void removeEscrowToken(long handle, int uid) {
}
@Override
public void isEscrowTokenActive(long handle, int uid) {
}
};
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(ADDRESS);
mContext = InstrumentationRegistry.getTargetContext();
mCarTrustedDeviceService = new CarTrustedDeviceService(mContext);
mCarTrustAgentEnrollmentService = new CarTrustAgentEnrollmentService(mContext,
mCarTrustedDeviceService, mMockCarTrustAgentBleManager);
mCarTrustedDeviceService.init();
mCarTrustAgentEnrollmentService.init();
mCarTrustAgentEnrollmentService.setEnrollmentRequestDelegate(mEnrollDelegate);
mUserId = new CarUserManagerHelper(mContext).getCurrentProcessUserId();
mCarTrustAgentEnrollmentService.onRemoteDeviceConnected(mBluetoothDevice);
}
@After
public void tearDown() {
// Need to clear the shared preference
mCarTrustAgentEnrollmentService.onEscrowTokenRemoved(TEST_HANDLE1, mUserId);
mCarTrustAgentEnrollmentService.onEscrowTokenRemoved(TEST_HANDLE2, mUserId);
}
@Test
public void testDisableEnrollment_startEnrollmentAdvertisingFail() {
mCarTrustAgentEnrollmentService.setTrustedDeviceEnrollmentEnabled(false);
mCarTrustAgentEnrollmentService.startEnrollmentAdvertising();
verify(mMockCarTrustAgentBleManager, never()).startEnrollmentAdvertising(anyString(),
any(AdvertiseCallback.class));
}
@Test
public void testEncryptionHandshake() {
mCarTrustAgentEnrollmentService.setEncryptionRunner(
EncryptionRunnerFactory.newDummyRunner());
assertThat(mCarTrustAgentEnrollmentService.mEnrollmentState).isEqualTo(
mCarTrustAgentEnrollmentService.ENROLLMENT_STATE_NONE);
// Have received device unique id.
UUID uuid = UUID.randomUUID();
mCarTrustAgentEnrollmentService.onDataReceived(Utils.uuidToBytes(uuid));
assertThat(mCarTrustAgentEnrollmentService.mEnrollmentState).isEqualTo(
mCarTrustAgentEnrollmentService.ENROLLMENT_STATE_UNIQUE_ID);
assertThat(mCarTrustAgentEnrollmentService.mEncryptionState).isEqualTo(
HandshakeMessage.HandshakeState.UNKNOWN);
// send device unique id
verify(mMockCarTrustAgentBleManager).sendMessage(
eq(Utils.uuidToBytes(mCarTrustedDeviceService.getUniqueId())), any(), eq(false),
any(SendMessageCallback.class));
// Have received handshake request.
mCarTrustAgentEnrollmentService.onDataReceived(
DummyEncryptionRunner.INIT.getBytes());
assertThat(mCarTrustAgentEnrollmentService.mEncryptionState).isEqualTo(
HandshakeMessage.HandshakeState.IN_PROGRESS);
// Send response to init handshake request
verify(mMockCarTrustAgentBleManager).sendMessage(
eq(DummyEncryptionRunner.INIT_RESPONSE.getBytes()), any(), eq(false),
any(SendMessageCallback.class));
mCarTrustAgentEnrollmentService.onDataReceived(
DummyEncryptionRunner.CLIENT_RESPONSE.getBytes());
assertThat(mCarTrustAgentEnrollmentService.mEncryptionState).isEqualTo(
HandshakeMessage.HandshakeState.VERIFICATION_NEEDED);
verify(mMockCarTrustAgentBleManager).sendMessage(
eq(DummyEncryptionRunner.INIT_RESPONSE.getBytes()), any(), eq(false),
any(SendMessageCallback.class));
// Completed the handshake by confirming the verification code.
mCarTrustAgentEnrollmentService.enrollmentHandshakeAccepted(mBluetoothDevice);
verify(mMockCarTrustAgentBleManager).sendMessage(
eq(mCarTrustAgentEnrollmentService.CONFIRMATION_SIGNAL), any(), eq(false),
any(SendMessageCallback.class));
assertThat(mCarTrustAgentEnrollmentService.mEncryptionState).isEqualTo(
HandshakeMessage.HandshakeState.FINISHED);
assertThat(mCarTrustAgentEnrollmentService.mEnrollmentState).isEqualTo(
mCarTrustAgentEnrollmentService.ENROLLMENT_STATE_ENCRYPTION_COMPLETED);
}
@Test
public void testOnEscrowTokenAdded_tokenRequiresActivation() {
mCarTrustAgentEnrollmentService.onEscrowTokenAdded(TEST_TOKEN.getBytes(), TEST_HANDLE1,
mUserId);
// Token has not been activated, the number of enrolled trusted device will remain the same.
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).isEmpty();
assertThat(mCarTrustAgentEnrollmentService.isEscrowTokenActive(TEST_HANDLE1,
mUserId)).isFalse();
}
@Test
public void testOnEscrowTokenActiveStateChange_true_addTrustedDevice() {
// Set up the service to go through the handshake
setupEncryptionHandshake(TEST_ID1);
// Token has been activated and added to the shared preference
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE1, /* isTokenActive= */ true, mUserId);
verify(mMockCarTrustAgentBleManager).sendMessage(any(),
any(), eq(true), any(SendMessageCallback.class));
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).containsExactly(DEVICE_INFO1);
assertThat(mCarTrustAgentEnrollmentService.isEscrowTokenActive(TEST_HANDLE1,
mUserId)).isTrue();
}
@Test
public void testOnEscrowTokenActiveStateChange_addDuplicateDevice() {
// Set up the service to go through the handshake
setupEncryptionHandshake(TEST_ID1);
// Enroll device
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE1, /* isTokenActive= */ true, mUserId);
// Enroll same device again
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE2, /* isTokenActive= */ true, mUserId);
verify(mEnrollDelegate).removeEscrowToken(eq(TEST_HANDLE1), eq(mUserId));
}
@Test
public void testOnEscrowTokenActiveStateChange_false_doNotAddTrustedDevice() {
// Set up the service to go through the handshake
setupEncryptionHandshake(TEST_ID1);
// Token activation fail
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE1, /* isTokenActive= */ false, mUserId);
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).isEmpty();
}
@Test
public void testOnEscrowTokenRemoved_removeOneTrustedDevice() {
setupEncryptionHandshake(TEST_ID1);
SharedPreferences sharedPrefs = mCarTrustedDeviceService.getSharedPrefs();
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE1, /* isTokenActive= */ true,
mUserId);
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).containsExactly(DEVICE_INFO1);
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE1)).isEqualTo(
mUserId);
assertThat(sharedPrefs.getLong(TEST_ID1.toString(), -1)).isEqualTo(TEST_HANDLE1);
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE2, /* isTokenActive= */ true,
mUserId);
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).containsExactly(DEVICE_INFO1, DEVICE_INFO2);
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE2)).isEqualTo(
mUserId);
assertThat(sharedPrefs.getLong(TEST_ID1.toString(), -1)).isEqualTo(TEST_HANDLE2);
// Remove one handle
mCarTrustAgentEnrollmentService.onEscrowTokenRemoved(TEST_HANDLE1, mUserId);
assertThat(mCarTrustAgentEnrollmentService.getEnrolledDeviceInfosForUser(
mUserId)).containsExactly(DEVICE_INFO2);
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE1)).isEqualTo(-1);
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE2)).isEqualTo(
mUserId);
assertThat(sharedPrefs.getLong(TEST_ID1.toString(), -1)).isEqualTo(TEST_HANDLE2);
}
@Test
public void testGetUserHandleByTokenHandle_nonExistentHandle() {
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE1)).isEqualTo(-1);
}
@Test
public void testEncryptionKeyStorage() {
byte[] encryptionKey = KEY.getBytes();
if (mCarTrustedDeviceService.saveEncryptionKey(DEVICE_ID, encryptionKey)) {
assertThat(mCarTrustedDeviceService.getEncryptionKey(DEVICE_ID))
.isEqualTo(encryptionKey);
}
mCarTrustedDeviceService.clearEncryptionKey(DEVICE_ID);
assertThat(mCarTrustedDeviceService.getEncryptionKey(DEVICE_ID) == null).isTrue();
}
@Test
public void testGetUserHandleByTokenHandle_existingHandle() {
setupEncryptionHandshake(TEST_ID1);
mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(
TEST_HANDLE1, /* isTokenActive= */ true, mUserId);
assertThat(mCarTrustedDeviceService.getUserHandleByTokenHandle(TEST_HANDLE1)).isEqualTo(
mUserId);
}
private void setupEncryptionHandshake(UUID uuid) {
mCarTrustAgentEnrollmentService.setEncryptionRunner(
EncryptionRunnerFactory.newDummyRunner());
mCarTrustAgentEnrollmentService.onDataReceived(Utils.uuidToBytes(uuid));
mCarTrustAgentEnrollmentService.onDataReceived(
DummyEncryptionRunner.INIT.getBytes());
mCarTrustAgentEnrollmentService.onDataReceived(
DummyEncryptionRunner.CLIENT_RESPONSE.getBytes());
mCarTrustAgentEnrollmentService.enrollmentHandshakeAccepted(mBluetoothDevice);
}
}