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