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 package com.android.car.bluetooth; 17 18 import static com.android.car.bluetooth.FastPairAccountKeyStorage.AccountKey; 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 20 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothGatt; 24 import android.bluetooth.BluetoothGattCharacteristic; 25 import android.bluetooth.BluetoothGattDescriptor; 26 import android.bluetooth.BluetoothGattServer; 27 import android.bluetooth.BluetoothGattServerCallback; 28 import android.bluetooth.BluetoothGattService; 29 import android.bluetooth.BluetoothManager; 30 import android.bluetooth.BluetoothProfile; 31 import android.car.builtin.util.Slogf; 32 import android.content.BroadcastReceiver; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.IntentFilter; 36 import android.os.Handler; 37 import android.os.ParcelUuid; 38 import android.util.Base64; 39 import android.util.Log; 40 41 import com.android.car.CarLog; 42 import com.android.car.CarServiceUtils; 43 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 44 import com.android.car.internal.util.IndentingPrintWriter; 45 46 import java.math.BigInteger; 47 import java.nio.ByteBuffer; 48 import java.nio.ByteOrder; 49 import java.security.KeyFactory; 50 import java.security.KeyPairGenerator; 51 import java.security.MessageDigest; 52 import java.security.PrivateKey; 53 import java.security.PublicKey; 54 import java.security.interfaces.ECPublicKey; 55 import java.security.spec.ECParameterSpec; 56 import java.security.spec.ECPoint; 57 import java.security.spec.ECPrivateKeySpec; 58 import java.security.spec.ECPublicKeySpec; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.List; 62 import java.util.Objects; 63 import java.util.Random; 64 65 import javax.crypto.Cipher; 66 import javax.crypto.KeyAgreement; 67 import javax.crypto.spec.SecretKeySpec; 68 69 /** 70 * The FastPairGattServer is responsible for all 2 way communications with the Fast Pair Seeker. 71 * It is running in the background over BLE whenever the Fast Pair Service is running, waiting for a 72 * Seeker to connect, after which time it manages the authentication an performs the steps as 73 * required by the Fast Pair Specification. 74 */ 75 public class FastPairGattServer { 76 // Service ID assigned for FastPair. 77 public static final ParcelUuid FAST_PAIR_SERVICE_UUID = ParcelUuid 78 .fromString("0000FE2C-0000-1000-8000-00805f9b34fb"); 79 public static final ParcelUuid FAST_PAIR_MODEL_ID_UUID = ParcelUuid 80 .fromString("FE2C1233-8366-4814-8EB0-01DE32100BEA"); 81 public static final ParcelUuid KEY_BASED_PAIRING_UUID = ParcelUuid 82 .fromString("FE2C1234-8366-4814-8EB0-01DE32100BEA"); 83 public static final ParcelUuid PASSKEY_UUID = ParcelUuid 84 .fromString("FE2C1235-8366-4814-8EB0-01DE32100BEA"); 85 public static final ParcelUuid ACCOUNT_KEY_UUID = ParcelUuid 86 .fromString("FE2C1236-8366-4814-8EB0-01DE32100BEA"); 87 public static final ParcelUuid CLIENT_CHARACTERISTIC_CONFIG = ParcelUuid 88 .fromString("00002902-0000-1000-8000-00805f9b34fb"); 89 public static final ParcelUuid DEVICE_NAME_CHARACTERISTIC_CONFIG = ParcelUuid 90 .fromString("00002A00-0000-1000-8000-00805f9b34fb"); 91 private static final String TAG = CarLog.tagFor(FastPairGattServer.class); 92 private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); 93 private static final int MAX_KEY_COUNT = 10; 94 private static final int KEY_LIFESPAN_AWAIT_PAIRING = 60_000; 95 // Spec *does* say indefinitely but not having a timeout is risky. This matches the BT stack's 96 // internal pairing timeout 97 private static final int KEY_LIFESPAN_PAIRING = 35_000; 98 private static final int KEY_LIFESPAN_AWAIT_ACCOUNT_KEY = 10_000; 99 private static final int INVALID = -1; 100 101 private final boolean mAutomaticPasskeyConfirmation; 102 private final byte[] mModelId; 103 private final String mPrivateAntiSpoof; 104 private final Context mContext; 105 106 private final FastPairAccountKeyStorage mFastPairAccountKeyStorage; 107 108 private BluetoothGattServer mBluetoothGattServer; 109 private final BluetoothManager mBluetoothManager; 110 private final BluetoothAdapter mBluetoothAdapter; 111 private final Object mPasskeyLock = new Object(); 112 private int mSeekerPasskey = INVALID; 113 private int mPairingPasskey = INVALID; 114 private final DecryptionFailureCounter mFailureCounter = new DecryptionFailureCounter(); 115 private BluetoothGattService mFastPairService = new BluetoothGattService( 116 FAST_PAIR_SERVICE_UUID.getUuid(), BluetoothGattService.SERVICE_TYPE_PRIMARY); 117 private Callbacks mCallbacks; 118 private SecretKeySpec mSharedSecretKey; 119 private BluetoothDevice mLocalRpaDevice; 120 private BluetoothDevice mRemotePairingDevice; 121 private BluetoothDevice mRemoteGattDevice; 122 123 interface Callbacks { 124 /** 125 * Notify the Provider of completion to a GATT session 126 * @param successful 127 */ onPairingCompleted(boolean successful)128 void onPairingCompleted(boolean successful); 129 } 130 131 private class DecryptionFailureCounter { 132 public static final int FAILURE_LIMIT = 10; 133 private static final int FAILURE_RESET_TIMEOUT = 300_000; // 5 minutes 134 135 private int mCount = 0; 136 137 private Runnable mResetRunnable = new Runnable() { 138 @Override 139 public void run() { 140 Slogf.i(TAG, "Five minutes have expired. Reset failure count to 0"); 141 reset(); 142 } 143 }; 144 increment()145 public void increment() { 146 if (hasExceededLimit()) { 147 Slogf.w(TAG, "Failure count is already at the limit."); 148 return; 149 } 150 151 mCount++; 152 Slogf.i(TAG, "Failure count increased, failures=%d", mCount); 153 if (hasExceededLimit()) { 154 Slogf.w(TAG, "Failure count has reached 10, wait 5 minutes for more tries"); 155 mHandler.postDelayed(mResetRunnable, FAILURE_RESET_TIMEOUT); 156 } 157 } 158 reset()159 public void reset() { 160 Slogf.i(TAG, "Reset failure count"); 161 mHandler.removeCallbacks(mResetRunnable); 162 mCount = 0; 163 } 164 hasExceededLimit()165 public boolean hasExceededLimit() { 166 return mCount >= FAILURE_LIMIT; 167 } 168 169 @Override toString()170 public String toString() { 171 return String.valueOf(mCount); 172 } 173 } 174 175 /** 176 * Notify this FastPairGattServer of a new RPA from the FastPairAdvertiser 177 */ updateLocalRpa(BluetoothDevice device)178 public void updateLocalRpa(BluetoothDevice device) { 179 mLocalRpaDevice = device; 180 } 181 182 private Runnable mClearSharedSecretKey = new Runnable() { 183 @Override 184 public void run() { 185 Slogf.w(TAG, "Shared secret key has expired. Clearing key material."); 186 clearSharedSecretKey(); 187 } 188 }; 189 190 private final Handler mHandler = new Handler( 191 CarServiceUtils.getHandlerThread(FastPairProvider.THREAD_NAME).getLooper()); 192 private BluetoothGattCharacteristic mModelIdCharacteristic; 193 private BluetoothGattCharacteristic mKeyBasedPairingCharacteristic; 194 private BluetoothGattCharacteristic mPasskeyCharacteristic; 195 private BluetoothGattCharacteristic mAccountKeyCharacteristic; 196 private BluetoothGattCharacteristic mDeviceNameCharacteristic; 197 198 /** 199 * GATT server callbacks responsible for servicing read and write calls from the remote device 200 */ 201 private BluetoothGattServerCallback mBluetoothGattServerCallback = 202 new BluetoothGattServerCallback() { 203 @Override 204 public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { 205 super.onConnectionStateChange(device, status, newState); 206 if (DBG) { 207 Slogf.d(TAG, "onConnectionStateChange %d Device: %s", newState, device); 208 } 209 if (newState == BluetoothProfile.STATE_DISCONNECTED) { 210 invalidatePairingPasskeys(); 211 clearSharedSecretKey(); 212 mRemoteGattDevice = null; 213 mRemotePairingDevice = null; 214 mCallbacks.onPairingCompleted(false); 215 } else if (newState == BluetoothProfile.STATE_CONNECTED) { 216 mRemoteGattDevice = device; 217 218 // Although we're already connected, it's good practice to call connect again. This 219 // will mark the connection as desirable in the core stack so that it isn't 220 // accidentally torn down. 221 mBluetoothGattServer.connect(device, /* auto-connect= */ false); 222 } 223 } 224 225 @Override 226 public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, 227 BluetoothGattCharacteristic characteristic) { 228 super.onCharacteristicReadRequest(device, requestId, offset, characteristic); 229 if (DBG) { 230 Slogf.d(TAG, "onCharacteristicReadRequest"); 231 } 232 if (characteristic == mModelIdCharacteristic) { 233 if (DBG) { 234 Slogf.d(TAG, "reading model ID"); 235 } 236 } 237 mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, 238 characteristic.getValue()); 239 } 240 241 @Override 242 public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, 243 BluetoothGattCharacteristic characteristic, boolean preparedWrite, 244 boolean responseNeeded, 245 int offset, byte[] value) { 246 super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, 247 responseNeeded, offset, value); 248 if (DBG) { 249 Slogf.d(TAG, "onWrite, uuid=%s, length=%d", characteristic.getUuid(), 250 (value != null ? value.length : -1)); 251 } 252 253 if (characteristic == mKeyBasedPairingCharacteristic) { 254 if (DBG) { 255 Slogf.d(TAG, "onWriteKeyBasedPairingCharacteristic"); 256 } 257 byte[] response = processKeyBasedPairing(value); 258 if (response == null) { 259 Slogf.w(TAG, "Could not process key based pairing request. Ignoring."); 260 mBluetoothGattServer 261 .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, 262 null); 263 return; 264 } 265 mKeyBasedPairingCharacteristic.setValue(response); 266 mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 267 offset, response); 268 mBluetoothGattServer 269 .notifyCharacteristicChanged(device, mDeviceNameCharacteristic, false); 270 mBluetoothGattServer 271 .notifyCharacteristicChanged(device, mKeyBasedPairingCharacteristic, false); 272 273 } else if (characteristic == mPasskeyCharacteristic) { 274 if (DBG) { 275 Slogf.d(TAG, "onWritePasskey %s", characteristic.getUuid()); 276 } 277 processPairingKey(value); 278 mBluetoothGattServer 279 .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null); 280 } else if (characteristic == mAccountKeyCharacteristic) { 281 if (DBG) { 282 Slogf.d(TAG, "onWriteAccountKeyCharacteristic"); 283 } 284 processAccountKey(value); 285 286 mBluetoothGattServer 287 .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null); 288 } else { 289 Slogf.w(TAG, "onWriteOther %s", characteristic.getUuid()); 290 } 291 } 292 293 @Override 294 public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, 295 BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, 296 int offset, byte[] value) { 297 if (DBG) { 298 Slogf.d(TAG, "onDescriptorWriteRequest"); 299 } 300 mBluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, 301 descriptor.getValue()); 302 } 303 }; 304 305 /** 306 * Receive incoming pairing requests such that we can confirm Keys match. 307 */ 308 BroadcastReceiver mPairingAttemptsReceiver = new BroadcastReceiver() { 309 @Override 310 public void onReceive(Context context, Intent intent) { 311 String action = intent.getAction(); 312 if (DBG) { 313 Slogf.d(TAG, action); 314 } 315 316 switch (action) { 317 case BluetoothDevice.ACTION_PAIRING_REQUEST: 318 mRemotePairingDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 319 synchronized (mPasskeyLock) { 320 mPairingPasskey = 321 intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, INVALID); 322 if (DBG) { 323 Slogf.d(TAG, 324 "Pairing Request - device=%s, pin_code=%s, seeker_passkey=%s", 325 mRemotePairingDevice, mPairingPasskey, mSeekerPasskey); 326 } 327 328 if (!isConnected()) { 329 Slogf.d(TAG, "Received pairing request outside of a Fast Pair Session"); 330 break; 331 } 332 333 if (mPairingPasskey == INVALID) { 334 Slogf.w(TAG, "Received an invalid pin_code from the BT stack"); 335 break; 336 } 337 338 // The Seeker registers for passkey characteristic notifications after 339 // pairing begins on incoming pairings, so we hold our passkey write until 340 // we receive their passkey so we can be sure they have registered for 341 // notifications and will receive our passkey. 342 if (mSeekerPasskey != INVALID) { 343 sendPairingResponse(mPairingPasskey); 344 } else { 345 Slogf.w(TAG, "Got code from BT stack before getting Seeker's passkey"); 346 } 347 348 if (mSeekerPasskey != INVALID && mPairingPasskey != INVALID) { 349 comparePasskeys(); 350 } 351 } 352 // TODO (243578517): Abort the broadcast when everything is valid and we support 353 // automatic acceptance. 354 break; 355 356 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: 357 BluetoothDevice device = 358 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 359 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, INVALID); 360 int previousState = 361 intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, INVALID); 362 363 if (DBG) { 364 Slogf.d(TAG, "Bond State Change - device=%s, old_state=%s, new_state=%s", 365 device, BluetoothUtils.getBondStateName(previousState), 366 BluetoothUtils.getBondStateName(state)); 367 } 368 369 // If the bond state has changed for the device we're current fast pairing with 370 // and it is now bonded, then pairing is complete. Reset the failure count to 0. 371 // Await a potential account key. 372 if (device != null && device.equals(mRemotePairingDevice)) { 373 if (state == BluetoothDevice.BOND_BONDED) { 374 if (DBG) { 375 Slogf.d(TAG, "Pairing complete, device=%s", mRemotePairingDevice); 376 } 377 setSharedSecretKeyLifespan(KEY_LIFESPAN_AWAIT_ACCOUNT_KEY); 378 mRemotePairingDevice = null; 379 invalidatePairingPasskeys(); 380 mFailureCounter.reset(); 381 } else if (state == BluetoothDevice.BOND_NONE) { 382 if (DBG) { 383 Slogf.d(TAG, "Pairing attempt failed, device=%s", 384 mRemotePairingDevice); 385 } 386 mRemotePairingDevice = null; 387 invalidatePairingPasskeys(); 388 } 389 } 390 break; 391 392 case BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED: 393 String name = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME); 394 updateLocalName(name); 395 break; 396 397 default: 398 Slogf.w(TAG, "Unknown action. Skipped"); 399 break; 400 } 401 } 402 }; 403 404 /** 405 * FastPairGattServer 406 * @param context user specific context on which to make callse 407 * @param modelId assigned Fast Pair Model ID 408 * @param antiSpoof assigned Fast Pair private Anti Spoof key 409 * @param callbacks callbacks used to report back current pairing status 410 * @param automaticAcceptance automatically accept an incoming pairing request that has been 411 * authenticated through the Fast Pair protocol without further user interaction. 412 */ FastPairGattServer(Context context, int modelId, String antiSpoof, Callbacks callbacks, boolean automaticAcceptance, FastPairAccountKeyStorage fastPairAccountKeyStorage)413 FastPairGattServer(Context context, int modelId, String antiSpoof, 414 Callbacks callbacks, boolean automaticAcceptance, 415 FastPairAccountKeyStorage fastPairAccountKeyStorage) { 416 mContext = Objects.requireNonNull(context); 417 mFastPairAccountKeyStorage = Objects.requireNonNull(fastPairAccountKeyStorage); 418 mCallbacks = Objects.requireNonNull(callbacks); 419 mPrivateAntiSpoof = antiSpoof; 420 mAutomaticPasskeyConfirmation = automaticAcceptance; 421 mBluetoothManager = context.getSystemService(BluetoothManager.class); 422 mBluetoothAdapter = mBluetoothManager.getAdapter(); 423 ByteBuffer modelIdBytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt( 424 modelId); 425 mModelId = Arrays.copyOfRange(modelIdBytes.array(), 0, 3); 426 setup(); 427 } 428 429 /** 430 * Initialize all of the GATT characteristics with appropriate default values and the required 431 * configurations. 432 */ setup()433 private void setup() { 434 mModelIdCharacteristic = new BluetoothGattCharacteristic(FAST_PAIR_MODEL_ID_UUID.getUuid(), 435 BluetoothGattCharacteristic.PROPERTY_READ, 436 BluetoothGattCharacteristic.PERMISSION_READ); 437 mModelIdCharacteristic.setValue(mModelId); 438 mFastPairService.addCharacteristic(mModelIdCharacteristic); 439 440 mKeyBasedPairingCharacteristic = 441 new BluetoothGattCharacteristic(KEY_BASED_PAIRING_UUID.getUuid(), 442 BluetoothGattCharacteristic.PROPERTY_WRITE 443 | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 444 BluetoothGattCharacteristic.PERMISSION_WRITE); 445 mKeyBasedPairingCharacteristic.setValue(mModelId); 446 mKeyBasedPairingCharacteristic.addDescriptor(new BluetoothGattDescriptor( 447 CLIENT_CHARACTERISTIC_CONFIG.getUuid(), 448 BluetoothGattDescriptor.PERMISSION_READ 449 | BluetoothGattDescriptor.PERMISSION_WRITE)); 450 mFastPairService.addCharacteristic(mKeyBasedPairingCharacteristic); 451 452 mPasskeyCharacteristic = 453 new BluetoothGattCharacteristic(PASSKEY_UUID.getUuid(), 454 BluetoothGattCharacteristic.PROPERTY_WRITE 455 | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 456 BluetoothGattCharacteristic.PERMISSION_WRITE); 457 mPasskeyCharacteristic.setValue(mModelId); 458 mPasskeyCharacteristic.addDescriptor(new BluetoothGattDescriptor( 459 CLIENT_CHARACTERISTIC_CONFIG.getUuid(), 460 BluetoothGattDescriptor.PERMISSION_READ 461 | BluetoothGattDescriptor.PERMISSION_WRITE)); 462 463 mFastPairService.addCharacteristic(mPasskeyCharacteristic); 464 465 mAccountKeyCharacteristic = 466 new BluetoothGattCharacteristic(ACCOUNT_KEY_UUID.getUuid(), 467 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, 468 BluetoothGattCharacteristic.PERMISSION_WRITE); 469 mFastPairService.addCharacteristic(mAccountKeyCharacteristic); 470 471 mDeviceNameCharacteristic = 472 new BluetoothGattCharacteristic(DEVICE_NAME_CHARACTERISTIC_CONFIG.getUuid(), 473 BluetoothGattCharacteristic.PROPERTY_READ, 474 BluetoothGattCharacteristic.PERMISSION_READ); 475 String name = mBluetoothAdapter.getName(); 476 if (name == null) { 477 name = ""; 478 } 479 mDeviceNameCharacteristic.setValue(name); 480 mFastPairService.addCharacteristic(mDeviceNameCharacteristic); 481 } 482 updateLocalName(String name)483 void updateLocalName(String name) { 484 Slogf.d(TAG, "Device name changed to '%s'", name); 485 if (name != null) { 486 mDeviceNameCharacteristic.setValue(name); 487 } 488 } 489 490 /** 491 * Start the FastPairGattServer 492 * 493 * This makes the underlying service and characteristics available and registers us for events. 494 */ start()495 public synchronized boolean start() { 496 if (DBG) { 497 Slogf.d(TAG, "start()"); 498 } 499 500 if (isStarted()) { 501 Slogf.w(TAG, "GATT service already started"); 502 return true; 503 } 504 505 mBluetoothGattServer = mBluetoothManager 506 .openGattServer(mContext, mBluetoothGattServerCallback); 507 508 if (mBluetoothGattServer == null) { 509 Slogf.e(TAG, "Start failed, could not get a GATT server."); 510 return false; 511 } 512 513 // Setup filter to receive pairing attempts and passkey. Make this a high priority broadcast 514 // receiver so others can't intercept it before we can handle it. 515 IntentFilter filter = new IntentFilter(); 516 filter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST); 517 filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); 518 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 519 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 520 mContext.registerReceiver(mPairingAttemptsReceiver, filter); 521 522 mBluetoothGattServer.addService(mFastPairService); 523 return true; 524 } 525 526 /** 527 * Stop the FastPairGattServer 528 * 529 * This removes our underlying service and clears our state. 530 */ stop()531 public synchronized boolean stop() { 532 if (DBG) { 533 Slogf.d(TAG, "stop()"); 534 } 535 536 if (!isStarted()) { 537 Slogf.w(TAG, "GATT service already stopped"); 538 return true; 539 } 540 541 mContext.unregisterReceiver(mPairingAttemptsReceiver); 542 543 if (isConnected()) { 544 mBluetoothGattServer.cancelConnection(mRemoteGattDevice); 545 mRemoteGattDevice = null; 546 mCallbacks.onPairingCompleted(false); 547 } 548 549 invalidatePairingPasskeys(); 550 clearSharedSecretKey(); 551 552 mBluetoothGattServer.removeService(mFastPairService); 553 mBluetoothGattServer.close(); 554 mBluetoothGattServer = null; 555 return true; 556 } 557 558 /** 559 * Check if this service is started 560 */ isStarted()561 public boolean isStarted() { 562 return mBluetoothGattServer != null; 563 } 564 565 /** 566 * Check if a client is connected to this GATT server 567 * @return true if connected; 568 */ isConnected()569 public boolean isConnected() { 570 if (DBG) { 571 Slogf.d(TAG, "isConnected() -> %s", (mRemoteGattDevice != null)); 572 } 573 return (mRemoteGattDevice != null); 574 } 575 setSharedSecretKey(SecretKeySpec key, int lifespan)576 private void setSharedSecretKey(SecretKeySpec key, int lifespan) { 577 if (key == null) { 578 Slogf.w(TAG, "Cannot set a null shared secret."); 579 return; 580 } 581 Slogf.i(TAG, "Shared secret key set, key=%s lifespan=%d", key, lifespan); 582 mSharedSecretKey = key; 583 setSharedSecretKeyLifespan(lifespan); 584 } 585 setSharedSecretKeyLifespan(int lifespan)586 private void setSharedSecretKeyLifespan(int lifespan) { 587 if (mSharedSecretKey == null) { 588 Slogf.w(TAG, "Ignoring lifespan on null key"); 589 return; 590 } 591 if (DBG) { 592 Slogf.d(TAG, "Update key lifespan to %d", lifespan); 593 } 594 mHandler.removeCallbacks(mClearSharedSecretKey); 595 if (lifespan > 0) { 596 mHandler.postDelayed(mClearSharedSecretKey, lifespan); 597 } 598 } 599 clearSharedSecretKey()600 private void clearSharedSecretKey() { 601 Slogf.i(TAG, "Shared secret key has been cleared"); 602 mHandler.removeCallbacks(mClearSharedSecretKey); 603 mSharedSecretKey = null; 604 } 605 invalidatePairingPasskeys()606 private void invalidatePairingPasskeys() { 607 synchronized (mPasskeyLock) { 608 mPairingPasskey = INVALID; 609 mSeekerPasskey = INVALID; 610 } 611 } 612 isFastPairSessionActive()613 public boolean isFastPairSessionActive() { 614 return mSharedSecretKey != null; 615 } 616 617 /** 618 * Attempt to encrypt the provided data with the provided key 619 * 620 * @param data data to be encrypted 621 * @param secretKeySpec key to ecrypt the data with 622 * @return encrypted data upon success; null otherwise 623 */ encrypt(byte[] data, SecretKeySpec secretKeySpec)624 private byte[] encrypt(byte[] data, SecretKeySpec secretKeySpec) { 625 if (secretKeySpec == null) { 626 Slogf.e(TAG, "Encryption failed: no key"); 627 return null; 628 } 629 try { 630 Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); 631 cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 632 return cipher.doFinal(data); 633 634 } catch (Exception e) { 635 Slogf.e(TAG, "Encryption failed: %s", e); 636 } 637 return null; 638 } 639 /** 640 * Attempt to decrypt the provided data with the provided key 641 * 642 * @param encryptedData data to be decrypted 643 * @param secretKeySpec key to decrypt the data with 644 * @return decrypted data upon success; null otherwise 645 */ decrypt(byte[] encryptedData, SecretKeySpec secretKeySpec)646 private byte[] decrypt(byte[] encryptedData, SecretKeySpec secretKeySpec) { 647 if (secretKeySpec == null) { 648 Slogf.e(TAG, "Decryption failed: no key"); 649 return null; 650 } 651 try { 652 Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); 653 cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); 654 return cipher.doFinal(encryptedData); 655 656 } catch (Exception e) { 657 Slogf.e(TAG, "Decryption Failed: %s", e); 658 } 659 return null; 660 } 661 662 /** 663 * Determine if this pairing request is based on the anti-spoof keys associated with the model 664 * id or stored account keys. 665 * 666 * @param accountKey 667 * @return 668 */ processKeyBasedPairing(byte[] pairingRequest)669 private byte[] processKeyBasedPairing(byte[] pairingRequest) { 670 if (mFailureCounter.hasExceededLimit()) { 671 Slogf.w(TAG, "Failure count has exceeded 10. Ignoring Key-Based Pairing requests"); 672 return null; 673 } 674 675 if (pairingRequest == null) { 676 Slogf.w(TAG, "Received a null pairing request"); 677 mFailureCounter.increment(); 678 clearSharedSecretKey(); 679 return null; 680 } 681 682 List<SecretKeySpec> possibleKeys = new ArrayList<>(); 683 if (pairingRequest.length == 80) { 684 if (DBG) { 685 Slogf.d(TAG, "Use Anti-spoofing key"); 686 } 687 // if the pairingRequest is 80 bytes long try the anit-spoof key 688 final byte[] remotePublicKey = Arrays.copyOfRange(pairingRequest, 16, 80); 689 690 possibleKeys 691 .add(calculateAntiSpoofing(Base64.decode(mPrivateAntiSpoof, 0), remotePublicKey) 692 .getKeySpec()); 693 } else if (pairingRequest.length == 16) { 694 if (DBG) { 695 Slogf.d(TAG, "Use stored account keys"); 696 } 697 // otherwise the pairing request is the encrypted request, try all the stored account 698 // keys 699 List<AccountKey> storedAccountKeys = mFastPairAccountKeyStorage.getAllAccountKeys(); 700 for (AccountKey key : storedAccountKeys) { 701 possibleKeys.add(new SecretKeySpec(key.toBytes(), "AES")); 702 } 703 } else { 704 Slogf.w(TAG, "Received key based pairing request of invalid length %d", 705 pairingRequest.length); 706 mFailureCounter.increment(); 707 clearSharedSecretKey(); 708 return null; 709 } 710 711 byte[] encryptedRequest = Arrays.copyOfRange(pairingRequest, 0, 16); 712 if (DBG) { 713 Slogf.d(TAG, "Checking %d Keys", possibleKeys.size()); 714 } 715 // check all the keys for a valid pairing request 716 for (SecretKeySpec key : possibleKeys) { 717 if (DBG) { 718 Slogf.d(TAG, "Checking possible key"); 719 } 720 if (validateRequestAgainstKey(encryptedRequest, key)) { 721 // If the key was able to decrypt the request and the addresses match then set it as 722 // the shared secret and set a lifespan timeout 723 setSharedSecretKey(key, KEY_LIFESPAN_AWAIT_PAIRING); 724 725 // Use the key to craft encrypted response to the seeker with the local public 726 // address and salt. If encryption goes wrong, move on to the next key 727 String localAddress = mBluetoothAdapter.getAddress(); 728 byte[] localAddressBytes = BluetoothUtils.getBytesFromAddress(localAddress); 729 byte[] rawResponse = new byte[16]; 730 new Random().nextBytes(rawResponse); 731 rawResponse[0] = 0x01; 732 System.arraycopy(localAddressBytes, 0, rawResponse, 1, 6); 733 byte[] response = encrypt(rawResponse, key); 734 if (response == null) { 735 clearSharedSecretKey(); 736 return null; 737 } 738 return response; 739 } 740 } 741 Slogf.w(TAG, "No matching key found"); 742 mFailureCounter.increment(); 743 clearSharedSecretKey(); 744 return null; 745 } 746 747 /** 748 * New pairings based upon model ID requires the Fast Pair provider to authenticate to that the 749 * seeker it is in possession of the private key associated with the model ID advertised. This 750 * is accomplished via Eliptic-curve Diffie-Hellman 751 * 752 * @param localPrivateKey 753 * @param remotePublicKey 754 * @return 755 */ calculateAntiSpoofing(byte[] localPrivateKey, byte[] remotePublicKey)756 private AccountKey calculateAntiSpoofing(byte[] localPrivateKey, byte[] remotePublicKey) { 757 try { 758 if (DBG) { 759 Slogf.d(TAG, "Calculating secret key from remote public key"); 760 } 761 // Initialize the EC key generator 762 KeyFactory keyFactory = KeyFactory.getInstance("EC"); 763 KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); 764 ECParameterSpec ecParameterSpec = ((ECPublicKey) kpg.generateKeyPair().getPublic()) 765 .getParams(); 766 // Use the private anti-spoofing key 767 ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec( 768 new BigInteger(1, localPrivateKey), 769 ecParameterSpec); 770 // Calculate the public point utilizing the data received from the remote device 771 ECPoint publicPoint = new ECPoint(new BigInteger(1, Arrays.copyOf(remotePublicKey, 32)), 772 new BigInteger(1, Arrays.copyOfRange(remotePublicKey, 32, 64))); 773 ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(publicPoint, ecParameterSpec); 774 PrivateKey privateKey = keyFactory.generatePrivate(ecPrivateKeySpec); 775 PublicKey publicKey = keyFactory.generatePublic(ecPublicKeySpec); 776 777 // Generate a shared secret 778 KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH"); 779 keyAgreement.init(privateKey); 780 keyAgreement.doPhase(publicKey, true); 781 byte[] sharedSecret = keyAgreement.generateSecret(); 782 783 // Use the first 16 bytes of a hash of the shared secret as the session key 784 final byte[] digest = MessageDigest.getInstance("SHA-256").digest(sharedSecret); 785 786 byte[] AESAntiSpoofingKey = Arrays.copyOf(digest, 16); 787 if (DBG) { 788 Slogf.d(TAG, "Key calculated"); 789 } 790 return new AccountKey(AESAntiSpoofingKey); 791 } catch (Exception e) { 792 Slogf.w(TAG, "Error calculating anti-spoofing key: %s", e); 793 return null; 794 } 795 } 796 797 /** 798 * Check if the given key can be used to decrypt the pairing request and prove the request is 799 * valid. 800 * 801 * A request is valid if its decrypted value is of type 0x00 or 0x10 and it contains either the 802 * seekers public or current BLE address. If a key successfully decrypts and validates a request 803 * then that is the key we should use as our shared secret key. 804 * 805 * @param encryptedRequest the request to decrypt and validate 806 * @param secretKeySpec the key to use while attempting to decrypt the request 807 * @return true if the key matches, false otherwise 808 */ validateRequestAgainstKey(byte[] encryptedRequest, SecretKeySpec secretKeySpec)809 private boolean validateRequestAgainstKey(byte[] encryptedRequest, 810 SecretKeySpec secretKeySpec) { 811 // Decrypt the request 812 byte[] decryptedRequest = decrypt(encryptedRequest, secretKeySpec); 813 if (decryptedRequest == null) { 814 return false; 815 } 816 817 if (DBG) { 818 StringBuilder sb = new StringBuilder(); 819 for (byte b : decryptedRequest) { 820 sb.append(String.format("%02X ", b)); 821 } 822 Slogf.d(TAG, "Decrypted Request=[ %s]", sb.toString()); 823 } 824 // Check that the request is either a Key-based Pairing Request or an Action Request 825 if (decryptedRequest[0] == 0x00 || decryptedRequest[0] == 0x10) { 826 String localAddress = mBluetoothAdapter.getAddress(); 827 byte[] localAddressBytes = BluetoothUtils.getBytesFromAddress(localAddress); 828 // Extract the remote address bytes from the message 829 byte[] remoteAddressBytes = Arrays.copyOfRange(decryptedRequest, 2, 8); 830 BluetoothDevice localDevice = mBluetoothAdapter.getRemoteDevice(localAddress); 831 BluetoothDevice reportedDevice = mBluetoothAdapter.getRemoteDevice(remoteAddressBytes); 832 if (DBG) { 833 Slogf.d(TAG, "rpa=%s, public=%s, reported=%s", mLocalRpaDevice, localAddress, 834 reportedDevice); 835 } 836 if (mLocalRpaDevice == null) { 837 Slogf.w(TAG, "Cannot get own address"); 838 } 839 // Test that the received device address matches this devices address 840 if (reportedDevice.equals(localDevice) || reportedDevice.equals(mLocalRpaDevice)) { 841 if (DBG) { 842 Slogf.d(TAG, "SecretKey Validated"); 843 } 844 return encryptedRequest != null; 845 } 846 } 847 return false; 848 } 849 850 /** 851 * Extract the 6 digit Bluetooth Simple Secure Passkey from the received message and confirm 852 * it matches the key received through the Bluetooth pairing procedure. 853 * 854 * If the passkeys match and automatic passkey confirmation is enabled, approve of the pairing. 855 * If the passkeys do not match reject the pairing and invalidate our key material. 856 * 857 * @param pairingKey 858 * @return true if the procedure completed, although pairing may not have been approved 859 */ processPairingKey(byte[] pairingKey)860 private boolean processPairingKey(byte[] pairingKey) { 861 if (pairingKey == null || pairingKey.length != 16) { 862 clearSharedSecretKey(); 863 return false; 864 } 865 866 byte[] decryptedRequest = decrypt(pairingKey, mSharedSecretKey); 867 if (decryptedRequest == null) { 868 clearSharedSecretKey(); 869 return false; 870 } 871 synchronized (mPasskeyLock) { 872 mSeekerPasskey = Byte.toUnsignedInt(decryptedRequest[1]) * 65536 873 + Byte.toUnsignedInt(decryptedRequest[2]) * 256 874 + Byte.toUnsignedInt(decryptedRequest[3]); 875 876 if (DBG) { 877 Slogf.d(TAG, "Received passkey request, type=%s, passkey=%d, our_passkey=%d", 878 decryptedRequest[0], mSeekerPasskey, mPairingPasskey); 879 } 880 881 // The Seeker registers for passkey characteristic notifications after pairing begins 882 // on incoming pairings, so we hold our passkey write until we receive their passkey so 883 // we can be sure they have registered for notifications and will receive our passkey. 884 if (mPairingPasskey != INVALID) { 885 sendPairingResponse(mPairingPasskey); 886 } else { 887 if (DBG) { 888 Slogf.d(TAG, "Got Seeker's passkey before receiving pin code from BT stack"); 889 } 890 } 891 892 if (mSeekerPasskey != INVALID && mPairingPasskey != INVALID) { 893 comparePasskeys(); 894 } 895 } 896 897 return true; 898 } 899 900 /** 901 * Compares the BT Stack reported passkey to the Fast Pair Seeker reported passkey. 902 */ comparePasskeys()903 private void comparePasskeys() { 904 synchronized (mPasskeyLock) { 905 if (mPairingPasskey == INVALID || mSeekerPasskey == INVALID) { 906 Slogf.w(TAG, "Mising passkey to compare, bt=%s, seeker=%s", mPairingPasskey, 907 mSeekerPasskey); 908 return; 909 } 910 if (mPairingPasskey == mSeekerPasskey) { 911 if (DBG) { 912 Slogf.d(TAG, "Passkeys match, auto_accept=%s", mAutomaticPasskeyConfirmation); 913 } 914 if (mAutomaticPasskeyConfirmation) { 915 mRemotePairingDevice.setPairingConfirmation(true); 916 } 917 } else { 918 Slogf.w(TAG, "Passkeys don't match, rejecting"); 919 mRemotePairingDevice.setPairingConfirmation(false); 920 clearSharedSecretKey(); 921 } 922 } 923 } 924 925 /** 926 * Send the seeker the pin code we received so they can validate it. Encrypt it with our shared 927 * secret. 928 * 929 * @param passkey the key-based pairing passkey, as described by the core BT specification 930 */ sendPairingResponse(int passkey)931 private void sendPairingResponse(int passkey) { 932 if (!isConnected()) return; 933 if (DBG) { 934 Slogf.d(TAG, "sendPairingResponse %d", passkey); 935 } 936 937 // Once pairing begins, we can hold on to the shared secret key until pairing 938 // completes 939 setSharedSecretKeyLifespan(KEY_LIFESPAN_PAIRING); 940 941 // Send an encrypted response to the seeker with the Bluetooth passkey as required 942 byte[] decryptedResponse = new byte[16]; 943 new Random().nextBytes(decryptedResponse); 944 ByteBuffer pairingPasskeyBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt( 945 passkey); 946 decryptedResponse[0] = 0x3; 947 decryptedResponse[1] = pairingPasskeyBytes.get(1); 948 decryptedResponse[2] = pairingPasskeyBytes.get(2); 949 decryptedResponse[3] = pairingPasskeyBytes.get(3); 950 951 byte[] response = encrypt(decryptedResponse, mSharedSecretKey); 952 if (response == null) { 953 clearSharedSecretKey(); 954 return; 955 } 956 mPasskeyCharacteristic.setValue(response); 957 mBluetoothGattServer 958 .notifyCharacteristicChanged(mRemoteGattDevice, mPasskeyCharacteristic, false); 959 } 960 961 /** 962 * The final step of the Fast Pair procedure involves receiving an account key from the 963 * Fast Pair seeker, authenticating it, and then storing it for future use. Only one attempt 964 * at writing this key is allowed by the spec. Discard the shared secret after this one attempt. 965 * 966 * @param accountKey the account key, encrypted with our sharded secret 967 */ processAccountKey(byte[] accountKey)968 private void processAccountKey(byte[] accountKey) { 969 if (accountKey == null || accountKey.length != 16) { 970 clearSharedSecretKey(); 971 return; 972 } 973 974 byte[] decodedAccountKey = decrypt(accountKey, mSharedSecretKey); 975 if (decodedAccountKey != null && decodedAccountKey[0] == 0x04) { 976 AccountKey receivedKey = new AccountKey(decodedAccountKey); 977 if (DBG) { 978 Slogf.d(TAG, "Received Account Key, key=%s", receivedKey); 979 } 980 mFastPairAccountKeyStorage.add(receivedKey); 981 } else { 982 if (DBG) { 983 Slogf.d(TAG, "Received invalid Account Key"); 984 } 985 } 986 987 // Always clear the shared secret key following any attempt to write an account key 988 clearSharedSecretKey(); 989 } 990 991 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)992 void dump(IndentingPrintWriter writer) { 993 writer.println("FastPairGattServer:"); 994 writer.increaseIndent(); 995 writer.println("Started : " + isStarted()); 996 writer.println("Active : " + isFastPairSessionActive()); 997 writer.println("Currently connected to : " + mRemoteGattDevice); 998 writer.println("Failure counter : " + mFailureCounter); 999 writer.decreaseIndent(); 1000 } 1001 } 1002