1 /* 2 * Copyright (C) 2022 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 android.nearby.fastpair.provider; 18 19 import static com.android.server.nearby.common.bluetooth.fastpair.AesEcbSingleBlockEncryption.decrypt; 20 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.BLUETOOTH_ADDRESS_LENGTH; 21 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC; 22 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.DEVICE_ACTION; 23 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING; 24 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME; 25 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE; 26 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR; 27 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.ADDITIONAL_DATA_TYPE_INDEX; 28 29 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType; 30 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request; 31 32 import java.security.GeneralSecurityException; 33 import java.util.Arrays; 34 35 /** 36 * A wrapper for Fast Pair Provider to access decoded handshake request from the Seeker. 37 * 38 * @see {go/fast-pair-early-spec-handshake} 39 */ 40 public class HandshakeRequest { 41 42 /** 43 * 16 bytes data: 1-byte for type, 1-byte for flags, 6-bytes for provider's BLE address, 8 bytes 44 * optional data. 45 * 46 * @see {go/fast-pair-spec-handshake-message1} 47 */ 48 private final byte[] mDecryptedMessage; 49 50 /** Enumerates the handshake message types. */ 51 public enum Type { 52 KEY_BASED_PAIRING_REQUEST(Request.TYPE_KEY_BASED_PAIRING_REQUEST), 53 ACTION_OVER_BLE(Request.TYPE_ACTION_OVER_BLE), 54 UNKNOWN((byte) 0xFF); 55 56 private final byte mValue; 57 Type(byte type)58 Type(byte type) { 59 mValue = type; 60 } 61 getValue()62 public byte getValue() { 63 return mValue; 64 } 65 valueOf(byte value)66 public static Type valueOf(byte value) { 67 for (Type type : Type.values()) { 68 if (type.getValue() == value) { 69 return type; 70 } 71 } 72 return UNKNOWN; 73 } 74 } 75 HandshakeRequest(byte[] key, byte[] encryptedPairingRequest)76 public HandshakeRequest(byte[] key, byte[] encryptedPairingRequest) 77 throws GeneralSecurityException { 78 mDecryptedMessage = decrypt(key, encryptedPairingRequest); 79 } 80 81 /** 82 * Gets the type of this handshake request. Currently, we have 2 types: 0x00 for Key-based 83 * Pairing Request and 0x10 for Action Request. 84 */ getType()85 public Type getType() { 86 return Type.valueOf(mDecryptedMessage[Request.TYPE_INDEX]); 87 } 88 89 /** 90 * Gets verification data of this handshake request. 91 * Currently, we use Provider's BLE address. 92 */ getVerificationData()93 public byte[] getVerificationData() { 94 return Arrays.copyOfRange( 95 mDecryptedMessage, 96 Request.VERIFICATION_DATA_INDEX, 97 Request.VERIFICATION_DATA_INDEX + Request.VERIFICATION_DATA_LENGTH); 98 } 99 100 /** Gets Seeker's public address of the handshake request. */ getSeekerPublicAddress()101 public byte[] getSeekerPublicAddress() { 102 return Arrays.copyOfRange( 103 mDecryptedMessage, 104 Request.SEEKER_PUBLIC_ADDRESS_INDEX, 105 Request.SEEKER_PUBLIC_ADDRESS_INDEX + BLUETOOTH_ADDRESS_LENGTH); 106 } 107 108 /** Checks whether the Seeker request discoverability from flags byte. */ requestDiscoverable()109 public boolean requestDiscoverable() { 110 return (getFlags() & REQUEST_DISCOVERABLE) != 0; 111 } 112 113 /** 114 * Checks whether the Seeker requests that the Provider shall initiate bonding from flags byte. 115 */ requestProviderInitialBonding()116 public boolean requestProviderInitialBonding() { 117 return (getFlags() & PROVIDER_INITIATES_BONDING) != 0; 118 } 119 120 /** Checks whether the Seeker requests that the Provider shall notify the existing name. */ requestDeviceName()121 public boolean requestDeviceName() { 122 return (getFlags() & REQUEST_DEVICE_NAME) != 0; 123 } 124 125 /** Checks whether this is for retroactively writing account key. */ requestRetroactivePair()126 public boolean requestRetroactivePair() { 127 return (getFlags() & REQUEST_RETROACTIVE_PAIR) != 0; 128 } 129 130 /** Gets the flags of this handshake request. */ getFlags()131 private byte getFlags() { 132 return mDecryptedMessage[Request.FLAGS_INDEX]; 133 } 134 135 /** Checks whether the Seeker requests a device action. */ requestDeviceAction()136 public boolean requestDeviceAction() { 137 return (getFlags() & DEVICE_ACTION) != 0; 138 } 139 140 /** 141 * Checks whether the Seeker requests an action which will be followed by an additional data 142 * . 143 */ requestFollowedByAdditionalData()144 public boolean requestFollowedByAdditionalData() { 145 return (getFlags() & ADDITIONAL_DATA_CHARACTERISTIC) != 0; 146 } 147 148 /** Gets the {@link AdditionalDataType} of this handshake request. */ 149 @AdditionalDataType getAdditionalDataType()150 public int getAdditionalDataType() { 151 if (!requestFollowedByAdditionalData() 152 || mDecryptedMessage.length <= ADDITIONAL_DATA_TYPE_INDEX) { 153 return AdditionalDataType.UNKNOWN; 154 } 155 return mDecryptedMessage[ADDITIONAL_DATA_TYPE_INDEX]; 156 } 157 } 158