1 /* 2 * Copyright (C) 2019 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.car.trust; 18 19 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_HANDSHAKE_FAILURE; 20 import static android.car.trust.CarTrustAgentEnrollmentManager.ENROLLMENT_NOT_ALLOWED; 21 22 import static com.android.car.trust.EventLog.ENCRYPTION_KEY_SAVED; 23 import static com.android.car.trust.EventLog.ENROLLMENT_ENCRYPTION_STATE; 24 import static com.android.car.trust.EventLog.ENROLLMENT_HANDSHAKE_ACCEPTED; 25 import static com.android.car.trust.EventLog.ESCROW_TOKEN_ADDED; 26 import static com.android.car.trust.EventLog.RECEIVED_DEVICE_ID; 27 import static com.android.car.trust.EventLog.REMOTE_DEVICE_CONNECTED; 28 import static com.android.car.trust.EventLog.SHOW_VERIFICATION_CODE; 29 import static com.android.car.trust.EventLog.START_ENROLLMENT_ADVERTISING; 30 import static com.android.car.trust.EventLog.STOP_ENROLLMENT_ADVERTISING; 31 import static com.android.car.trust.EventLog.logEnrollmentEvent; 32 33 import android.annotation.IntDef; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.app.ActivityManager; 37 import android.bluetooth.BluetoothDevice; 38 import android.car.encryptionrunner.EncryptionRunner; 39 import android.car.encryptionrunner.EncryptionRunnerFactory; 40 import android.car.encryptionrunner.HandshakeException; 41 import android.car.encryptionrunner.HandshakeMessage; 42 import android.car.encryptionrunner.HandshakeMessage.HandshakeState; 43 import android.car.encryptionrunner.Key; 44 import android.car.trust.ICarTrustAgentBleCallback; 45 import android.car.trust.ICarTrustAgentEnrollment; 46 import android.car.trust.ICarTrustAgentEnrollmentCallback; 47 import android.car.trust.TrustedDeviceInfo; 48 import android.content.Context; 49 import android.content.SharedPreferences; 50 import android.os.IBinder; 51 import android.os.RemoteException; 52 import android.util.Log; 53 54 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType; 55 import com.android.car.R; 56 import com.android.car.Utils; 57 import com.android.internal.annotations.GuardedBy; 58 import com.android.internal.annotations.VisibleForTesting; 59 60 import java.io.PrintWriter; 61 import java.lang.annotation.Retention; 62 import java.lang.annotation.RetentionPolicy; 63 import java.security.SignatureException; 64 import java.util.ArrayList; 65 import java.util.HashMap; 66 import java.util.HashSet; 67 import java.util.Iterator; 68 import java.util.LinkedList; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Queue; 72 import java.util.Set; 73 import java.util.UUID; 74 75 /** 76 * A service that is part of the CarTrustedDeviceService that is responsible for allowing a 77 * phone to enroll as a trusted device. The enrolled phone can then be used for authenticating a 78 * user on the HU. This implements the {@link android.car.trust.CarTrustAgentEnrollmentManager} 79 * APIs that an app like Car Settings can call to conduct an enrollment. 80 */ 81 public class CarTrustAgentEnrollmentService extends ICarTrustAgentEnrollment.Stub { 82 private static final String TAG = "CarTrustAgentEnroll"; 83 private static final String TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY = 84 "trusted_device_enrollment_enabled"; 85 @VisibleForTesting 86 static final byte[] CONFIRMATION_SIGNAL = "True".getBytes(); 87 //Arbirary log size 88 private static final int MAX_LOG_SIZE = 20; 89 // This delimiter separates deviceId and deviceInfo, so it has to differ from the 90 // TrustedDeviceInfo delimiter. Once new API can be added, deviceId will be added to 91 // TrustedDeviceInfo and this delimiter will be removed. 92 private static final char DEVICE_INFO_DELIMITER = '#'; 93 94 private final CarTrustedDeviceService mTrustedDeviceService; 95 // List of clients listening to Enrollment state change events. 96 private final List<EnrollmentStateClient> mEnrollmentStateClients = new ArrayList<>(); 97 // List of clients listening to BLE state changes events during enrollment. 98 private final List<BleStateChangeClient> mBleStateChangeClients = new ArrayList<>(); 99 private final Queue<String> mLogQueue = new LinkedList<>(); 100 101 private final CarTrustAgentBleManager mCarTrustAgentBleManager; 102 private CarTrustAgentEnrollmentRequestDelegate mEnrollmentDelegate; 103 104 private Object mRemoteDeviceLock = new Object(); 105 @GuardedBy("mRemoteDeviceLock") 106 private BluetoothDevice mRemoteEnrollmentDevice; 107 private final Map<Long, Boolean> mTokenActiveStateMap = new HashMap<>(); 108 private String mClientDeviceName; 109 private String mClientDeviceId; 110 private final Context mContext; 111 112 private EncryptionRunner mEncryptionRunner = EncryptionRunnerFactory.newRunner(); 113 private HandshakeMessage mHandshakeMessage; 114 private Key mEncryptionKey; 115 private long mHandle; 116 @VisibleForTesting 117 @HandshakeState 118 int mEncryptionState = HandshakeState.UNKNOWN; 119 // State of last message sent to phone in enrollment process. Order matters with 120 // state being auto-incremented. 121 static final int ENROLLMENT_STATE_NONE = 0; 122 static final int ENROLLMENT_STATE_UNIQUE_ID = 1; 123 static final int ENROLLMENT_STATE_ENCRYPTION_COMPLETED = 2; 124 static final int ENROLLMENT_STATE_HANDLE = 3; 125 126 /** @hide */ 127 @VisibleForTesting 128 @Retention(RetentionPolicy.SOURCE) 129 @IntDef({ENROLLMENT_STATE_NONE, ENROLLMENT_STATE_UNIQUE_ID, 130 ENROLLMENT_STATE_ENCRYPTION_COMPLETED, ENROLLMENT_STATE_HANDLE}) 131 @interface EnrollmentState { 132 } 133 134 @VisibleForTesting 135 @EnrollmentState 136 int mEnrollmentState; 137 CarTrustAgentEnrollmentService(Context context, CarTrustedDeviceService service, CarTrustAgentBleManager bleService)138 public CarTrustAgentEnrollmentService(Context context, CarTrustedDeviceService service, 139 CarTrustAgentBleManager bleService) { 140 mContext = context; 141 mTrustedDeviceService = service; 142 mCarTrustAgentBleManager = bleService; 143 } 144 init()145 public synchronized void init() { 146 mCarTrustAgentBleManager.setupEnrollmentBleServer(); 147 } 148 149 /** 150 * Pass a dummy encryption to generate a dummy key, only for test purpose. 151 */ 152 @VisibleForTesting setEncryptionRunner(EncryptionRunner dummyEncryptionRunner)153 void setEncryptionRunner(EncryptionRunner dummyEncryptionRunner) { 154 mEncryptionRunner = dummyEncryptionRunner; 155 } 156 release()157 public synchronized void release() { 158 for (EnrollmentStateClient client : mEnrollmentStateClients) { 159 client.mListenerBinder.unlinkToDeath(client, 0); 160 } 161 for (BleStateChangeClient client : mBleStateChangeClients) { 162 client.mListenerBinder.unlinkToDeath(client, 0); 163 } 164 mEnrollmentStateClients.clear(); 165 } 166 167 // Implementing the ICarTrustAgentEnrollment interface 168 169 /** 170 * Begin BLE advertisement for Enrollment. This should be called from an app that conducts 171 * the enrollment of the trusted device. 172 */ 173 @Override startEnrollmentAdvertising()174 public void startEnrollmentAdvertising() { 175 if (!mTrustedDeviceService.getSharedPrefs() 176 .getBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, true)) { 177 Log.e(TAG, "Trusted Device Enrollment disabled"); 178 dispatchEnrollmentFailure(ENROLLMENT_NOT_ALLOWED); 179 return; 180 } 181 // Stop any current broadcasts 182 mTrustedDeviceService.getCarTrustAgentUnlockService().stopUnlockAdvertising(); 183 stopEnrollmentAdvertising(); 184 185 logEnrollmentEvent(START_ENROLLMENT_ADVERTISING); 186 addEnrollmentServiceLog("startEnrollmentAdvertising"); 187 mCarTrustAgentBleManager.startEnrollmentAdvertising(); 188 mEnrollmentState = ENROLLMENT_STATE_NONE; 189 } 190 191 /** 192 * Stop BLE advertisement for Enrollment 193 */ 194 @Override stopEnrollmentAdvertising()195 public void stopEnrollmentAdvertising() { 196 logEnrollmentEvent(STOP_ENROLLMENT_ADVERTISING); 197 addEnrollmentServiceLog("stopEnrollmentAdvertising"); 198 mCarTrustAgentBleManager.stopEnrollmentAdvertising(); 199 } 200 201 /** 202 * Called by the client to notify that the user has accepted a pairing code or any out-of-band 203 * confirmation, and send confirmation signals to remote bluetooth device. 204 * 205 * @param device the remote Bluetooth device that will receive the signal. 206 */ 207 @Override enrollmentHandshakeAccepted(BluetoothDevice device)208 public void enrollmentHandshakeAccepted(BluetoothDevice device) { 209 logEnrollmentEvent(ENROLLMENT_HANDSHAKE_ACCEPTED); 210 addEnrollmentServiceLog("enrollmentHandshakeAccepted"); 211 if (device == null || !device.equals(mRemoteEnrollmentDevice)) { 212 Log.wtf(TAG, 213 "Enrollment Failure: device is different from cached remote bluetooth device," 214 + " disconnect from the device. current device is:" + device); 215 mCarTrustAgentBleManager.disconnectRemoteDevice(); 216 return; 217 } 218 mCarTrustAgentBleManager.sendEnrollmentMessage(mRemoteEnrollmentDevice, CONFIRMATION_SIGNAL, 219 OperationType.ENCRYPTION_HANDSHAKE, /* isPayloadEncrypted= */ false); 220 setEnrollmentHandshakeAccepted(); 221 } 222 223 /** 224 * Terminate the Enrollment process. To be called when an error is encountered during 225 * enrollment. For example - user pressed cancel on pairing code confirmation or user 226 * navigated away from the app before completing enrollment. 227 */ 228 @Override terminateEnrollmentHandshake()229 public void terminateEnrollmentHandshake() { 230 addEnrollmentServiceLog("terminateEnrollmentHandshake"); 231 // Disconnect from BLE 232 mCarTrustAgentBleManager.disconnectRemoteDevice(); 233 // Remove any handles that have not been activated yet. 234 Iterator<Map.Entry<Long, Boolean>> it = mTokenActiveStateMap.entrySet().iterator(); 235 while (it.hasNext()) { 236 Map.Entry<Long, Boolean> pair = it.next(); 237 boolean isHandleActive = pair.getValue(); 238 if (!isHandleActive) { 239 long handle = pair.getKey(); 240 int uid = mTrustedDeviceService.getSharedPrefs().getInt(String.valueOf(handle), -1); 241 removeEscrowToken(handle, uid); 242 it.remove(); 243 } 244 } 245 } 246 247 /* 248 * Returns if there is an active token for the given user and handle. 249 * 250 * @param handle handle corresponding to the escrow token 251 * @param uid user id 252 * @return True if the escrow token is active, false if not 253 */ 254 @Override isEscrowTokenActive(long handle, int uid)255 public boolean isEscrowTokenActive(long handle, int uid) { 256 if (mTokenActiveStateMap.get(handle) != null) { 257 return mTokenActiveStateMap.get(handle); 258 } 259 return false; 260 } 261 262 /** 263 * Remove the Token associated with the given handle for the given user. 264 * 265 * @param handle handle corresponding to the escrow token 266 * @param uid user id 267 */ 268 @Override removeEscrowToken(long handle, int uid)269 public void removeEscrowToken(long handle, int uid) { 270 mEnrollmentDelegate.removeEscrowToken(handle, uid); 271 addEnrollmentServiceLog("removeEscrowToken (handle:" + handle + " uid:" + uid + ")"); 272 } 273 274 /** 275 * Remove all Trusted devices associated with the given user. 276 * 277 * @param uid user id 278 */ 279 @Override removeAllTrustedDevices(int uid)280 public void removeAllTrustedDevices(int uid) { 281 for (TrustedDeviceInfo device : getEnrolledDeviceInfosForUser(uid)) { 282 removeEscrowToken(device.getHandle(), uid); 283 } 284 } 285 286 /** 287 * Enable or disable enrollment of a Trusted device. When disabled, 288 * {@link android.car.trust.CarTrustAgentEnrollmentManager#ENROLLMENT_NOT_ALLOWED} is returned, 289 * when {@link #startEnrollmentAdvertising()} is called by a client. 290 * 291 * @param isEnabled {@code true} to enable; {@code false} to disable the feature. 292 */ 293 @Override setTrustedDeviceEnrollmentEnabled(boolean isEnabled)294 public void setTrustedDeviceEnrollmentEnabled(boolean isEnabled) { 295 SharedPreferences.Editor editor = mTrustedDeviceService.getSharedPrefs().edit(); 296 editor.putBoolean(TRUSTED_DEVICE_ENROLLMENT_ENABLED_KEY, isEnabled); 297 if (!editor.commit()) { 298 Log.wtf(TAG, 299 "Enrollment Failure: Commit to SharedPreferences failed. Enable? " + isEnabled); 300 } 301 } 302 303 /** 304 * Enable or disable authentication of the head unit with a trusted device. 305 * 306 * @param isEnabled when set to {@code false}, head unit will not be 307 * discoverable to unlock the user. Setting it to {@code true} will enable it 308 * back. 309 */ 310 @Override setTrustedDeviceUnlockEnabled(boolean isEnabled)311 public void setTrustedDeviceUnlockEnabled(boolean isEnabled) { 312 mTrustedDeviceService.getCarTrustAgentUnlockService() 313 .setTrustedDeviceUnlockEnabled(isEnabled); 314 } 315 316 /** 317 * Get the Handles and Device Mac Address corresponding to the token for the current user. The 318 * client can use this to list the trusted devices for the user. 319 * 320 * @param uid user id 321 * @return array of trusted device handles and names for the user. 322 */ 323 @NonNull 324 @Override getEnrolledDeviceInfosForUser(int uid)325 public List<TrustedDeviceInfo> getEnrolledDeviceInfosForUser(int uid) { 326 Set<String> enrolledDeviceInfos = mTrustedDeviceService.getSharedPrefs().getStringSet( 327 String.valueOf(uid), new HashSet<>()); 328 List<TrustedDeviceInfo> trustedDeviceInfos = new ArrayList<>(enrolledDeviceInfos.size()); 329 for (String deviceInfoWithId : enrolledDeviceInfos) { 330 TrustedDeviceInfo deviceInfo = extractDeviceInfo(deviceInfoWithId); 331 if (deviceInfo != null) { 332 trustedDeviceInfos.add(deviceInfo); 333 } 334 } 335 return trustedDeviceInfos; 336 } 337 338 /** 339 * Registers a {@link ICarTrustAgentEnrollmentCallback} to be notified for changes to the 340 * enrollment state. 341 * 342 * @param listener {@link ICarTrustAgentEnrollmentCallback} 343 */ 344 @Override registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener)345 public synchronized void registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener) { 346 if (listener == null) { 347 throw new IllegalArgumentException("Listener is null"); 348 } 349 // If a new client is registering, create a new EnrollmentStateClient and add it to the list 350 // of listening clients. 351 EnrollmentStateClient client = findEnrollmentStateClientLocked(listener); 352 if (client == null) { 353 client = new EnrollmentStateClient(listener); 354 try { 355 listener.asBinder().linkToDeath(client, 0); 356 } catch (RemoteException e) { 357 Log.e(TAG, "Cannot link death recipient to binder ", e); 358 return; 359 } 360 mEnrollmentStateClients.add(client); 361 } 362 } 363 364 /** 365 * Called after the escrow token has been successfully added to the framework. 366 * 367 * @param token the escrow token which has been added 368 * @param handle the given handle of that token 369 * @param uid the current user id 370 */ onEscrowTokenAdded(byte[] token, long handle, int uid)371 void onEscrowTokenAdded(byte[] token, long handle, int uid) { 372 if (Log.isLoggable(TAG, Log.DEBUG)) { 373 Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid); 374 } 375 376 if (mRemoteEnrollmentDevice == null) { 377 Log.e(TAG, "onEscrowTokenAdded() but no remote device connected!"); 378 removeEscrowToken(handle, uid); 379 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 380 return; 381 } 382 383 mTokenActiveStateMap.put(handle, false); 384 for (EnrollmentStateClient client : mEnrollmentStateClients) { 385 try { 386 client.mListener.onEscrowTokenAdded(handle); 387 } catch (RemoteException e) { 388 Log.e(TAG, "onEscrowTokenAdded dispatch failed", e); 389 } 390 } 391 } 392 393 /** 394 * Called after the escrow token has been successfully removed from the framework. 395 */ onEscrowTokenRemoved(long handle, int uid)396 void onEscrowTokenRemoved(long handle, int uid) { 397 if (Log.isLoggable(TAG, Log.DEBUG)) { 398 Log.d(TAG, "onEscrowTokenRemoved handle:" + handle + " uid:" + uid); 399 } 400 for (EnrollmentStateClient client : mEnrollmentStateClients) { 401 try { 402 client.mListener.onEscrowTokenRemoved(handle); 403 } catch (RemoteException e) { 404 Log.e(TAG, "onEscrowTokenRemoved dispatch failed", e); 405 } 406 } 407 SharedPreferences sharedPrefs = mTrustedDeviceService.getSharedPrefs(); 408 SharedPreferences.Editor editor = sharedPrefs.edit(); 409 editor.remove(String.valueOf(handle)); 410 Set<String> deviceInfos = sharedPrefs.getStringSet(String.valueOf(uid), new HashSet<>()); 411 Iterator<String> iterator = deviceInfos.iterator(); 412 while (iterator.hasNext()) { 413 String deviceIdAndInfo = iterator.next(); 414 TrustedDeviceInfo info = extractDeviceInfo(deviceIdAndInfo); 415 if (info != null && info.getHandle() == handle) { 416 if (Log.isLoggable(TAG, Log.DEBUG)) { 417 Log.d(TAG, "Removing trusted device: " + info); 418 } 419 String clientDeviceId = extractDeviceId(deviceIdAndInfo); 420 if (clientDeviceId != null && sharedPrefs.getLong(clientDeviceId, -1) == handle) { 421 editor.remove(clientDeviceId); 422 } 423 iterator.remove(); 424 break; 425 } 426 } 427 editor.putStringSet(String.valueOf(uid), deviceInfos); 428 if (!editor.commit()) { 429 Log.e(TAG, "EscrowToken removed, but shared prefs update failed"); 430 } 431 } 432 433 /** 434 * @param handle the handle whose active state change 435 * @param isTokenActive the active state of the handle 436 * @param uid id of current user 437 */ onEscrowTokenActiveStateChanged(long handle, boolean isTokenActive, int uid)438 void onEscrowTokenActiveStateChanged(long handle, boolean isTokenActive, int uid) { 439 if (Log.isLoggable(TAG, Log.DEBUG)) { 440 Log.d(TAG, "onEscrowTokenActiveStateChanged: " + Long.toHexString(handle)); 441 } 442 if (mRemoteEnrollmentDevice == null || !isTokenActive) { 443 if (mRemoteEnrollmentDevice == null) { 444 Log.e(TAG, 445 "Device disconnected before sending back handle. Enrollment incomplete"); 446 } 447 if (!isTokenActive) { 448 Log.e(TAG, "Unexpected: Escrow Token activation failed"); 449 } 450 removeEscrowToken(handle, uid); 451 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 452 return; 453 } 454 455 // Avoid storing duplicate info for same device by checking if there is already device info 456 // and deleting it. 457 SharedPreferences sharedPrefs = mTrustedDeviceService.getSharedPrefs(); 458 if (sharedPrefs.contains(mClientDeviceId)) { 459 removeEscrowToken(sharedPrefs.getLong(mClientDeviceId, -1), uid); 460 } 461 mTokenActiveStateMap.put(handle, isTokenActive); 462 Set<String> deviceInfo = sharedPrefs.getStringSet(String.valueOf(uid), new HashSet<>()); 463 String clientDeviceName; 464 if (mRemoteEnrollmentDevice.getName() != null) { 465 clientDeviceName = mRemoteEnrollmentDevice.getName(); 466 } else if (mClientDeviceName != null) { 467 clientDeviceName = mClientDeviceName; 468 } else { 469 clientDeviceName = mContext.getString(R.string.trust_device_default_name); 470 } 471 StringBuffer log = new StringBuffer() 472 .append("trustedDeviceAdded (id:").append(mClientDeviceId) 473 .append(", handle:").append(handle) 474 .append(", uid:").append(uid) 475 .append(", addr:").append(mRemoteEnrollmentDevice.getAddress()) 476 .append(", name:").append(clientDeviceName).append(")"); 477 addEnrollmentServiceLog(log.toString()); 478 deviceInfo.add(serializeDeviceInfoWithId(new TrustedDeviceInfo(handle, 479 mRemoteEnrollmentDevice.getAddress(), clientDeviceName), mClientDeviceId)); 480 481 // To conveniently get the devices info regarding certain user. 482 SharedPreferences.Editor editor = sharedPrefs.edit(); 483 editor.putStringSet(String.valueOf(uid), deviceInfo); 484 if (!editor.commit()) { 485 Log.e(TAG, "Writing DeviceInfo to shared prefs Failed"); 486 removeEscrowToken(handle, uid); 487 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 488 return; 489 } 490 491 // To conveniently get the user id to unlock when handle is received. 492 editor.putInt(String.valueOf(handle), uid); 493 if (!editor.commit()) { 494 Log.e(TAG, "Writing (handle, uid) to shared prefs Failed"); 495 removeEscrowToken(handle, uid); 496 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 497 return; 498 } 499 500 // To check if the device has already been mapped to a handle 501 editor.putLong(mClientDeviceId, handle); 502 if (!editor.commit()) { 503 Log.e(TAG, "Writing (identifier, handle) to shared prefs Failed"); 504 removeEscrowToken(handle, uid); 505 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 506 return; 507 } 508 509 if (Log.isLoggable(TAG, Log.DEBUG)) { 510 Log.d(TAG, "Sending handle: " + handle); 511 } 512 mHandle = handle; 513 mCarTrustAgentBleManager.sendEnrollmentMessage(mRemoteEnrollmentDevice, 514 mEncryptionKey.encryptData(Utils.longToBytes(handle)), 515 OperationType.CLIENT_MESSAGE, /* isPayloadEncrypted= */ true); 516 } 517 onEnrollmentAdvertiseStartSuccess()518 void onEnrollmentAdvertiseStartSuccess() { 519 for (BleStateChangeClient client : mBleStateChangeClients) { 520 try { 521 client.mListener.onEnrollmentAdvertisingStarted(); 522 } catch (RemoteException e) { 523 Log.e(TAG, "onAdvertiseSuccess dispatch failed", e); 524 } 525 } 526 } 527 onEnrollmentAdvertiseStartFailure()528 void onEnrollmentAdvertiseStartFailure() { 529 for (BleStateChangeClient client : mBleStateChangeClients) { 530 try { 531 client.mListener.onEnrollmentAdvertisingFailed(); 532 } catch (RemoteException e) { 533 Log.e(TAG, "onAdvertiseSuccess dispatch failed", e); 534 } 535 } 536 } 537 538 /** 539 * Called when a device has been connected through bluetooth 540 * 541 * @param device the connected device 542 */ onRemoteDeviceConnected(BluetoothDevice device)543 void onRemoteDeviceConnected(BluetoothDevice device) { 544 logEnrollmentEvent(REMOTE_DEVICE_CONNECTED); 545 addEnrollmentServiceLog("onRemoteDeviceConnected (addr:" + device.getAddress() + ")"); 546 resetEncryptionState(); 547 mHandle = 0; 548 synchronized (mRemoteDeviceLock) { 549 mRemoteEnrollmentDevice = device; 550 } 551 for (BleStateChangeClient client : mBleStateChangeClients) { 552 try { 553 client.mListener.onBleEnrollmentDeviceConnected(device); 554 } catch (RemoteException e) { 555 Log.e(TAG, "onRemoteDeviceConnected dispatch failed", e); 556 } 557 } 558 mCarTrustAgentBleManager.stopEnrollmentAdvertising(); 559 } 560 onRemoteDeviceDisconnected(BluetoothDevice device)561 void onRemoteDeviceDisconnected(BluetoothDevice device) { 562 if (Log.isLoggable(TAG, Log.DEBUG)) { 563 Log.d(TAG, "Device Disconnected: " + device.getAddress() + " Enrollment State: " 564 + mEnrollmentState + " Encryption State: " + mEncryptionState); 565 } 566 addEnrollmentServiceLog("onRemoteDeviceDisconnected (addr:" + device.getAddress() + ")"); 567 addEnrollmentServiceLog( 568 "Enrollment State: " + mEnrollmentState + " EncryptionState: " + mEncryptionState); 569 resetEncryptionState(); 570 mHandle = 0; 571 synchronized (mRemoteDeviceLock) { 572 mRemoteEnrollmentDevice = null; 573 } 574 for (BleStateChangeClient client : mBleStateChangeClients) { 575 try { 576 client.mListener.onBleEnrollmentDeviceDisconnected(device); 577 } catch (RemoteException e) { 578 Log.e(TAG, "onRemoteDeviceDisconnected dispatch failed", e); 579 } 580 } 581 } 582 583 /** 584 * Called when data is received during enrollment process. 585 * 586 * @param value received data 587 */ onEnrollmentDataReceived(byte[] value)588 void onEnrollmentDataReceived(byte[] value) { 589 if (mEnrollmentDelegate == null) { 590 if (Log.isLoggable(TAG, Log.DEBUG)) { 591 Log.d(TAG, "Enrollment Delegate not set"); 592 } 593 return; 594 } 595 switch (mEnrollmentState) { 596 case ENROLLMENT_STATE_NONE: 597 if (!CarTrustAgentValidator.isValidEnrollmentDeviceId(value)) { 598 Log.e(TAG, "Device id rejected by validator."); 599 return; 600 } 601 notifyDeviceIdReceived(value); 602 logEnrollmentEvent(RECEIVED_DEVICE_ID); 603 break; 604 case ENROLLMENT_STATE_UNIQUE_ID: 605 try { 606 processInitEncryptionMessage(value); 607 } catch (HandshakeException e) { 608 Log.e(TAG, "HandshakeException during set up of encryption: ", e); 609 } 610 break; 611 case ENROLLMENT_STATE_ENCRYPTION_COMPLETED: 612 notifyEscrowTokenReceived(value); 613 break; 614 case ENROLLMENT_STATE_HANDLE: 615 // only activated handle can be sent to the connected remote device. 616 dispatchEscrowTokenActiveStateChanged(mHandle, true); 617 mCarTrustAgentBleManager.disconnectRemoteDevice(); 618 break; 619 default: 620 // Should never get here 621 break; 622 } 623 } 624 onDeviceNameRetrieved(String deviceName)625 void onDeviceNameRetrieved(String deviceName) { 626 mClientDeviceName = deviceName; 627 } 628 notifyDeviceIdReceived(byte[] id)629 private void notifyDeviceIdReceived(byte[] id) { 630 UUID deviceId = Utils.bytesToUUID(id); 631 if (deviceId == null) { 632 Log.e(TAG, "Invalid device id sent"); 633 return; 634 } 635 mClientDeviceId = deviceId.toString(); 636 if (Log.isLoggable(TAG, Log.DEBUG)) { 637 Log.d(TAG, "Received device id: " + mClientDeviceId); 638 } 639 UUID uniqueId = mTrustedDeviceService.getUniqueId(); 640 if (uniqueId == null) { 641 Log.e(TAG, "Cannot get Unique ID for the IHU"); 642 resetEnrollmentStateOnFailure(); 643 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 644 return; 645 } 646 if (Log.isLoggable(TAG, Log.DEBUG)) { 647 Log.d(TAG, "Sending device id: " + uniqueId.toString()); 648 } 649 mCarTrustAgentBleManager.sendEnrollmentMessage(mRemoteEnrollmentDevice, 650 Utils.uuidToBytes(uniqueId), OperationType.CLIENT_MESSAGE, 651 /* isPayloadEncrypted= */ false); 652 mEnrollmentState++; 653 } 654 notifyEscrowTokenReceived(byte[] token)655 private void notifyEscrowTokenReceived(byte[] token) { 656 try { 657 mEnrollmentDelegate.addEscrowToken( 658 mEncryptionKey.decryptData(token), ActivityManager.getCurrentUser()); 659 mEnrollmentState++; 660 logEnrollmentEvent(ESCROW_TOKEN_ADDED); 661 } catch (SignatureException e) { 662 Log.e(TAG, "Could not decrypt escrow token", e); 663 } 664 } 665 666 /** 667 * Processes the given message as one that will establish encryption for secure communication. 668 * 669 * <p>This method should be called continually until {@link #mEncryptionState} is 670 * {@link HandshakeState#FINISHED}, meaning an secure channel has been set up. 671 * 672 * @param message The message received from the connected device. 673 * @throws HandshakeException If an error was encountered during the handshake flow. 674 */ processInitEncryptionMessage(byte[] message)675 private void processInitEncryptionMessage(byte[] message) throws HandshakeException { 676 if (Log.isLoggable(TAG, Log.DEBUG)) { 677 Log.d(TAG, "Processing init encryption message."); 678 } 679 switch (mEncryptionState) { 680 case HandshakeState.UNKNOWN: 681 if (Log.isLoggable(TAG, Log.DEBUG)) { 682 Log.d(TAG, "Responding to handshake init request."); 683 } 684 685 mHandshakeMessage = mEncryptionRunner.respondToInitRequest(message); 686 mEncryptionState = mHandshakeMessage.getHandshakeState(); 687 mCarTrustAgentBleManager.sendEnrollmentMessage( 688 mRemoteEnrollmentDevice, mHandshakeMessage.getNextMessage(), 689 OperationType.ENCRYPTION_HANDSHAKE, /* isPayloadEncrypted= */ false); 690 691 logEnrollmentEvent(ENROLLMENT_ENCRYPTION_STATE, mEncryptionState); 692 break; 693 694 case HandshakeState.IN_PROGRESS: 695 if (Log.isLoggable(TAG, Log.DEBUG)) { 696 Log.d(TAG, "Continuing handshake."); 697 } 698 699 mHandshakeMessage = mEncryptionRunner.continueHandshake(message); 700 mEncryptionState = mHandshakeMessage.getHandshakeState(); 701 702 if (Log.isLoggable(TAG, Log.DEBUG)) { 703 Log.d(TAG, "Updated encryption state: " + mEncryptionState); 704 } 705 706 // The state is updated after a call to continueHandshake(). Thus, need to check 707 // if we're in the next stage. 708 if (mEncryptionState == HandshakeState.VERIFICATION_NEEDED) { 709 showVerificationCode(); 710 return; 711 } 712 mCarTrustAgentBleManager.sendEnrollmentMessage(mRemoteEnrollmentDevice, 713 mHandshakeMessage.getNextMessage(), OperationType.ENCRYPTION_HANDSHAKE, 714 /* isPayloadEncrypted= */ false); 715 break; 716 case HandshakeState.VERIFICATION_NEEDED: 717 Log.w(TAG, "Encountered VERIFICATION_NEEDED state when it should have been " 718 + "transitioned to after IN_PROGRESS."); 719 // This case should never happen because this state should occur right after 720 // a call to "continueHandshake". But just in case, call the appropriate method. 721 showVerificationCode(); 722 break; 723 724 case HandshakeState.FINISHED: 725 // Should never reach this case since this state should occur after a verification 726 // code has been accepted. But it should mean handshake is done and the message 727 // is one for the escrow token. 728 notifyEscrowTokenReceived(message); 729 break; 730 731 default: 732 Log.w(TAG, "Encountered invalid handshake state: " + mEncryptionState); 733 break; 734 } 735 } 736 showVerificationCode()737 private void showVerificationCode() { 738 if (Log.isLoggable(TAG, Log.DEBUG)) { 739 Log.d(TAG, "showVerificationCode(): " + mHandshakeMessage.getVerificationCode()); 740 } 741 742 for (EnrollmentStateClient client : mEnrollmentStateClients) { 743 try { 744 client.mListener.onAuthStringAvailable(mRemoteEnrollmentDevice, 745 mHandshakeMessage.getVerificationCode()); 746 } catch (RemoteException e) { 747 Log.e(TAG, "Broadcast verification code failed", e); 748 } 749 } 750 logEnrollmentEvent(SHOW_VERIFICATION_CODE); 751 } 752 753 /** 754 * Reset the whole enrollment state. Disconnects the peer device and removes any escrow token 755 * that has not been activated. 756 * 757 * <p>This method should be called from any stage in the middle of enrollment where we 758 * encounter a failure. 759 */ resetEnrollmentStateOnFailure()760 private void resetEnrollmentStateOnFailure() { 761 terminateEnrollmentHandshake(); 762 resetEncryptionState(); 763 } 764 765 /** 766 * Resets the encryption status of this service. 767 * 768 * <p>This method should be called each time a device connects so that a new handshake can be 769 * started and encryption keys exchanged. 770 */ resetEncryptionState()771 private void resetEncryptionState() { 772 mEncryptionRunner = EncryptionRunnerFactory.newRunner(); 773 mHandshakeMessage = null; 774 mEncryptionKey = null; 775 mEncryptionState = HandshakeState.UNKNOWN; 776 mEnrollmentState = ENROLLMENT_STATE_NONE; 777 } 778 setEnrollmentHandshakeAccepted()779 private synchronized void setEnrollmentHandshakeAccepted() { 780 if (mEncryptionRunner == null) { 781 Log.e(TAG, "Received notification that enrollment handshake was accepted, " 782 + "but encryption was never set up."); 783 return; 784 } 785 HandshakeMessage message; 786 try { 787 message = mEncryptionRunner.verifyPin(); 788 } catch (HandshakeException e) { 789 Log.e(TAG, "Error during PIN verification", e); 790 resetEnrollmentStateOnFailure(); 791 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 792 return; 793 } 794 795 if (message.getHandshakeState() != HandshakeState.FINISHED) { 796 Log.e(TAG, "Handshake not finished after calling verify PIN. Instead got state: " 797 + message.getHandshakeState()); 798 return; 799 } 800 801 mEncryptionState = HandshakeState.FINISHED; 802 mEncryptionKey = message.getKey(); 803 if (!mTrustedDeviceService.saveEncryptionKey(mClientDeviceId, mEncryptionKey.asBytes())) { 804 resetEnrollmentStateOnFailure(); 805 dispatchEnrollmentFailure(ENROLLMENT_HANDSHAKE_FAILURE); 806 return; 807 } 808 logEnrollmentEvent(ENCRYPTION_KEY_SAVED); 809 mEnrollmentState++; 810 } 811 812 /** 813 * Iterates through the list of registered Enrollment State Change clients - 814 * {@link EnrollmentStateClient} and finds if the given client is already registered. 815 * 816 * @param listener Listener to look for. 817 * @return the {@link EnrollmentStateClient} if found, null if not 818 */ 819 @Nullable findEnrollmentStateClientLocked( ICarTrustAgentEnrollmentCallback listener)820 private EnrollmentStateClient findEnrollmentStateClientLocked( 821 ICarTrustAgentEnrollmentCallback listener) { 822 IBinder binder = listener.asBinder(); 823 // Find the listener by comparing the binder object they host. 824 for (EnrollmentStateClient client : mEnrollmentStateClients) { 825 if (client.isHoldingBinder(binder)) { 826 return client; 827 } 828 } 829 return null; 830 } 831 832 /** 833 * Unregister the given Enrollment State Change listener 834 * 835 * @param listener client to unregister 836 */ 837 @Override unregisterEnrollmentCallback( ICarTrustAgentEnrollmentCallback listener)838 public synchronized void unregisterEnrollmentCallback( 839 ICarTrustAgentEnrollmentCallback listener) { 840 if (listener == null) { 841 throw new IllegalArgumentException("Listener is null"); 842 } 843 844 EnrollmentStateClient client = findEnrollmentStateClientLocked(listener); 845 if (client == null) { 846 Log.e(TAG, "unregisterEnrollmentCallback(): listener was not previously " 847 + "registered"); 848 return; 849 } 850 listener.asBinder().unlinkToDeath(client, 0); 851 mEnrollmentStateClients.remove(client); 852 } 853 854 /** 855 * Registers a {@link ICarTrustAgentBleCallback} to be notified for changes to the BLE state 856 * changes. 857 * 858 * @param listener {@link ICarTrustAgentBleCallback} 859 */ 860 @Override registerBleCallback(ICarTrustAgentBleCallback listener)861 public synchronized void registerBleCallback(ICarTrustAgentBleCallback listener) { 862 if (listener == null) { 863 throw new IllegalArgumentException("Listener is null"); 864 } 865 // If a new client is registering, create a new EnrollmentStateClient and add it to the list 866 // of listening clients. 867 BleStateChangeClient client = findBleStateClientLocked(listener); 868 if (client == null) { 869 client = new BleStateChangeClient(listener); 870 try { 871 listener.asBinder().linkToDeath(client, 0); 872 } catch (RemoteException e) { 873 Log.e(TAG, "Cannot link death recipient to binder " + e); 874 return; 875 } 876 mBleStateChangeClients.add(client); 877 } 878 } 879 880 /** 881 * Iterates through the list of registered BLE State Change clients - 882 * {@link BleStateChangeClient} and finds if the given client is already registered. 883 * 884 * @param listener Listener to look for. 885 * @return the {@link BleStateChangeClient} if found, null if not 886 */ 887 @Nullable findBleStateClientLocked( ICarTrustAgentBleCallback listener)888 private BleStateChangeClient findBleStateClientLocked( 889 ICarTrustAgentBleCallback listener) { 890 IBinder binder = listener.asBinder(); 891 // Find the listener by comparing the binder object they host. 892 for (BleStateChangeClient client : mBleStateChangeClients) { 893 if (client.isHoldingBinder(binder)) { 894 return client; 895 } 896 } 897 return null; 898 } 899 900 /** 901 * Unregister the given BLE State Change listener 902 * 903 * @param listener client to unregister 904 */ 905 @Override unregisterBleCallback(ICarTrustAgentBleCallback listener)906 public synchronized void unregisterBleCallback(ICarTrustAgentBleCallback listener) { 907 if (listener == null) { 908 throw new IllegalArgumentException("Listener is null"); 909 } 910 911 BleStateChangeClient client = findBleStateClientLocked(listener); 912 if (client == null) { 913 Log.e(TAG, "unregisterBleCallback(): listener was not previously " 914 + "registered"); 915 return; 916 } 917 listener.asBinder().unlinkToDeath(client, 0); 918 mBleStateChangeClients.remove(client); 919 } 920 921 /** 922 * The interface that an enrollment delegate has to implement to add/remove escrow tokens. 923 */ 924 interface CarTrustAgentEnrollmentRequestDelegate { 925 /** 926 * Add the given escrow token that was generated by the peer device that is being enrolled. 927 * 928 * @param token the 64 bit token 929 * @param uid user id 930 */ addEscrowToken(byte[] token, int uid)931 void addEscrowToken(byte[] token, int uid); 932 933 /** 934 * Remove the given escrow token. This should be called when removing a trusted device. 935 * 936 * @param handle the 64 bit token 937 * @param uid user id 938 */ removeEscrowToken(long handle, int uid)939 void removeEscrowToken(long handle, int uid); 940 941 /** 942 * Query if the token is active. The result is asynchronously delivered through a callback 943 * {@link CarTrustAgentEnrollmentService#onEscrowTokenActiveStateChanged(long, boolean, 944 * int)} 945 * 946 * @param handle the 64 bit token 947 * @param uid user id 948 */ isEscrowTokenActive(long handle, int uid)949 void isEscrowTokenActive(long handle, int uid); 950 } 951 setEnrollmentRequestDelegate(CarTrustAgentEnrollmentRequestDelegate delegate)952 void setEnrollmentRequestDelegate(CarTrustAgentEnrollmentRequestDelegate delegate) { 953 mEnrollmentDelegate = delegate; 954 } 955 dump(PrintWriter writer)956 void dump(PrintWriter writer) { 957 writer.println("*CarTrustAgentEnrollmentService*"); 958 writer.println("Enrollment Service Logs:"); 959 for (String log : mLogQueue) { 960 writer.println("\t" + log); 961 } 962 } 963 addEnrollmentServiceLog(String message)964 private void addEnrollmentServiceLog(String message) { 965 if (mLogQueue.size() >= MAX_LOG_SIZE) { 966 mLogQueue.remove(); 967 } 968 mLogQueue.add(System.currentTimeMillis() + " : " + message); 969 } 970 dispatchEscrowTokenActiveStateChanged(long handle, boolean active)971 private void dispatchEscrowTokenActiveStateChanged(long handle, boolean active) { 972 for (EnrollmentStateClient client : mEnrollmentStateClients) { 973 try { 974 client.mListener.onEscrowTokenActiveStateChanged(handle, active); 975 } catch (RemoteException e) { 976 Log.e(TAG, "Cannot notify client of a Token Activation change: " + active); 977 } 978 } 979 } 980 dispatchEnrollmentFailure(int error)981 private void dispatchEnrollmentFailure(int error) { 982 for (EnrollmentStateClient client : mEnrollmentStateClients) { 983 try { 984 client.mListener.onEnrollmentHandshakeFailure(null, error); 985 } catch (RemoteException e) { 986 Log.e(TAG, "onEnrollmentHandshakeFailure dispatch failed", e); 987 } 988 } 989 } 990 991 /** 992 * Currently, we store a map of uid -> a set of deviceId+deviceInfo strings 993 * This method extracts deviceInfo from a device+deviceInfo string, which should be 994 * created by {@link #serializeDeviceInfoWithId(TrustedDeviceInfo, String)} 995 * 996 * @param deviceInfoWithId deviceId+deviceInfo string 997 */ 998 @Nullable extractDeviceInfo(String deviceInfoWithId)999 private static TrustedDeviceInfo extractDeviceInfo(String deviceInfoWithId) { 1000 int delimiterIndex = deviceInfoWithId.indexOf(DEVICE_INFO_DELIMITER); 1001 if (delimiterIndex < 0) { 1002 return null; 1003 } 1004 return TrustedDeviceInfo.deserialize(deviceInfoWithId.substring(delimiterIndex + 1)); 1005 } 1006 1007 /** 1008 * Extract deviceId from a deviceId+deviceInfo string which should be created by 1009 * {@link #serializeDeviceInfoWithId(TrustedDeviceInfo, String)} 1010 * 1011 * @param deviceInfoWithId deviceId+deviceInfo string 1012 */ 1013 @Nullable extractDeviceId(String deviceInfoWithId)1014 private static String extractDeviceId(String deviceInfoWithId) { 1015 int delimiterIndex = deviceInfoWithId.indexOf(DEVICE_INFO_DELIMITER); 1016 if (delimiterIndex < 0) { 1017 return null; 1018 } 1019 return deviceInfoWithId.substring(0, delimiterIndex); 1020 } 1021 1022 // Create deviceId+deviceInfo string serializeDeviceInfoWithId(TrustedDeviceInfo info, String id)1023 private static String serializeDeviceInfoWithId(TrustedDeviceInfo info, String id) { 1024 return new StringBuilder() 1025 .append(id) 1026 .append(DEVICE_INFO_DELIMITER) 1027 .append(info.serialize()) 1028 .toString(); 1029 } 1030 1031 /** 1032 * Class that holds onto client related information - listener interface, process that hosts the 1033 * binder object etc. 1034 * <p> 1035 * It also registers for death notifications of the host. 1036 */ 1037 private class EnrollmentStateClient implements DeathRecipient { 1038 private final IBinder mListenerBinder; 1039 private final ICarTrustAgentEnrollmentCallback mListener; 1040 EnrollmentStateClient(ICarTrustAgentEnrollmentCallback listener)1041 EnrollmentStateClient(ICarTrustAgentEnrollmentCallback listener) { 1042 mListener = listener; 1043 mListenerBinder = listener.asBinder(); 1044 } 1045 1046 @Override binderDied()1047 public void binderDied() { 1048 if (Log.isLoggable(TAG, Log.DEBUG)) { 1049 Log.d(TAG, "Binder died " + mListenerBinder); 1050 } 1051 mListenerBinder.unlinkToDeath(this, 0); 1052 synchronized (CarTrustAgentEnrollmentService.this) { 1053 mEnrollmentStateClients.remove(this); 1054 } 1055 } 1056 1057 /** 1058 * Returns if the given binder object matches to what this client info holds. 1059 * Used to check if the listener asking to be registered is already registered. 1060 * 1061 * @return true if matches, false if not 1062 */ isHoldingBinder(IBinder binder)1063 public boolean isHoldingBinder(IBinder binder) { 1064 return mListenerBinder == binder; 1065 } 1066 } 1067 1068 private class BleStateChangeClient implements DeathRecipient { 1069 private final IBinder mListenerBinder; 1070 private final ICarTrustAgentBleCallback mListener; 1071 BleStateChangeClient(ICarTrustAgentBleCallback listener)1072 BleStateChangeClient(ICarTrustAgentBleCallback listener) { 1073 mListener = listener; 1074 mListenerBinder = listener.asBinder(); 1075 } 1076 1077 @Override binderDied()1078 public void binderDied() { 1079 if (Log.isLoggable(TAG, Log.DEBUG)) { 1080 Log.d(TAG, "Binder died " + mListenerBinder); 1081 } 1082 mListenerBinder.unlinkToDeath(this, 0); 1083 synchronized (CarTrustAgentEnrollmentService.this) { 1084 mBleStateChangeClients.remove(this); 1085 } 1086 } 1087 1088 /** 1089 * Returns if the given binder object matches to what this client info holds. 1090 * Used to check if the listener asking to be registered is already registered. 1091 * 1092 * @return true if matches, false if not 1093 */ isHoldingBinder(IBinder binder)1094 public boolean isHoldingBinder(IBinder binder) { 1095 return mListenerBinder == binder; 1096 } 1097 onEnrollmentAdvertisementStarted()1098 public void onEnrollmentAdvertisementStarted() { 1099 try { 1100 mListener.onEnrollmentAdvertisingStarted(); 1101 } catch (RemoteException e) { 1102 Log.e(TAG, "onEnrollmentAdvertisementStarted() failed", e); 1103 } 1104 } 1105 } 1106 } 1107