Google Fast Pair Service

Introduction

The Google Fast Pair Service (GFPS) utilizes Bluetooth Low Energy (BLE) to discover nearby Bluetooth devices without using significant phone battery, enabling “magical” scenarios based on device proximity. GFPS is initially aimed to facilitate first time pairing of audio sink devices, such as speakers, headphones and car kits, with as little user interaction required as possible. Subsequent pairing and reconnection will be seamless based on proximity.

Profile dependencies

This profile requires either the Advanced Audio Distribution Profile (A2DP) or the Hands-Free Profile (HFP).

The GFPS service implementation is compatible with the Bluetooth Core Specification v4.2 or later.

Octet order

Wherever a field consists of multiple bytes, the byte ordering is big-endian, that is, network byte order (most-significant octet to least-significant octet).

Note that while this is standard for bytes transferred over networks, it is different from the byte ordering for multi-byte fields in Bluetooth SIG specifications (for example, a service UUID in an advertisement is little-endian).

Configuration

Roles

The profile defines two roles: Fast Pair Seeker, and Fast Pair Provider. The Seeker is normally a phone, looking for an audio device to pair with. The Provider is an audio device that is advertising its presence and readiness to pair (e.g. a discoverable pair of headphones).

The Fast Pair Seeker shall use the GAP Central role. The Fast Pair Provider shall use the GAP Peripheral role. Additionally, the Fast Pair Provider shall implement one relevant Bluetooth Profile such as the Advanced Audio Distribution Profile (A2DP) or Hands-Free Profile (HFP).

Both the Fast Pair Seeker and Fast Pair Provider roles must support Bluetooth Classic BR/EDR bonding procedures.

Device discovery

To facilitate device discovery, the Fast Pair Provider shall advertise a payload indicating support for the Google Fast Pair Service (with data as described below). The Fast Pair Seeker shall periodically scan for and observe the presence of Fast Pair Provider advertising frames, and take action if interested.

Fast Pair Provider

Model Registration

All Provider models must be registered with Google before they will work with Fast Pair. After registration, Google will distribute a Model ID and Anti-Spoofing Public/Private Key Pair. The information provided during registration is used in the pairing suggestion presented to the user, and may be used in other UX.

Example pairing suggestion (subject to change)

For information about registering your device and getting the relevant Model ID and Anti-Spoofing keys, see the Nearby Devices documentation.

Transmit power

Provider devices should advertise at a low transmit power (TxPower) to limit exposure of the advertised device. However, the power shall be high enough such that the advertisement is visible by any phone at least 1 meter away.

To determine proximity, the Fast Pair Seeker must know the Fast Pair Provider’s transmit power. For the purposes of this profile, TxPower is defined as the received signal strength at source (0 meters), measured in dBm (this is the same way that Eddystone defines it).

This measured value shall be transmitted using one of these methods:

Included in the Advertisement Record
The device includes the Tx Power Level data type, ibid., § 1.5, in its advertisement.
Provided during model registration
The manufacturer provides Google with the transmit power, and the device model used to measure it, during Model Registration.
The device must keep its transmit power constant for all broadcasts when using this option so that distance measurements are accurate.

Keys

Anti-Spoofing Public/Private Key Pair

After model registration, along with the Model ID, Google will distribute a 256-bit Anti-Spoofing Private Key (a point on the secp256r1 elliptic curve). This key shall be persisted on the Provider device, and ideally stored inside a Secure Element (SE). Note that a Secure Element is strongly recommended—in the absence of one, there is no guarantee that attackers could not spoof the provider role, because the private key could leak. This key leaking opens up the possibility of man in the middle attacks; therefore, if impersonation or abuse is detected, Fast Pair features that use this key may be disabled (for example, the "Tap to pair" notification when the Provider is in pairing mode).

The corresponding Anti-Spoofing Public Key is not currently used by the Provider. It is used by the Seeker, to encrypt a message to send to the Provider (see Key-based Pairing).

Account Key List

The Provider shall allocate space to store a persisted list of 128-bit Account Keys. Each Account Key allows the Provider to be recognized as belonging to a certain user account.

The list must be able to store at least five keys (that is, there must be at least 80 bytes of space dedicated to this list). Providers can optionally store more than this, they just must make sure that the keys will fit inside of their advertising packet. The exact number that can be stored will depend on how many free bytes are available in the advertising packet; see the Account Key Filter section for more information on determining how many bytes each key will take up. For example, to advertise 10 account keys, 15 bytes need to be available in the packet.

This list is initially empty, and should be cleared if the Provider is factory-reset (if the user clears its paired device list). The list is populated as described in the Account Key characteristic section.

Advertising: When discoverable

When the Provider device is BR/EDR discoverable (that is, in pairing mode), it shall advertise Fast Pair Model ID Data over BLE.

Advertising interval

The interval between advertisements should be no larger than 100ms (10Hz). A fast rate allows the Seeker to quickly find the Provider, even when scanning in low-power mode.

BLE address

To prevent tracking, BLE advertising shall use the random resolvable private address (RPA). The address shall be rotated at minimum every 15 minutes while the device is actively advertising, and every time the state changes from not advertising to advertising. A randomized offset should be used to alter the address randomization interval.

Advertising payload: Fast Pair Model ID Data

The advertisement shall contain the Service Data data type, ibid., § 1.11. The UUID shall be the Fast Pair Service UUID of 0xFE2C. The service data shall contain the following:

Octet Data type Description Value
0-2 uint24 24-bit model ID varies
Model ID

Each Provider model has a 24-bit model ID, which is provided by Google during Model Registration.

Advertising: When not discoverable

When not discoverable (that is, not in pairing mode), the Provider device shall advertise Fast Pair Account Data, using the following guidelines.

Advertising the account data allows seekers nearby to recognize when a provider belongs to their account and initiate pairing without having to force the provider back into pairing mode first, which is a common cause for user complaint. Seekers will provide the opportunity for users to be able to ignore this broadcast in the case where they do not wait to pair with the provider or the broadcast is not relevant (for example, if they have already paired). Seekers will also filter out obviously bad broadcasts automatically, such as when the account data is misconfigured.

Advertising interval

The interval between advertisements should be at most 250ms (4Hz).

BLE address

To prevent tracking, BLE advertising shall use the random resolvable private address (RPA). The address shall be rotated at minimum every 15 minutes while the device is actively advertising, and every time the state changes from not advertising to advertising. A randomized offset should be used to alter the address randomization interval.

Advertising payload: Fast Pair Account Data

The advertisement shall contain the Service Data data type, Ibid., § 1.11. The UUID shall be the Fast Pair Service UUID of 0xFE2C. The service data shall contain the following:

Octet Data type Description Value Mandatory?
0 uint8 Flags 0x00
(all bits reserved for future use)
Mandatory
1 - varies Account Key Data Mandatory if the Account Key List is non-empty

The Account Key Data contains:

Octet Data type Description Value Mandatory?
0 uint8 Field length and type
0bLLLLTTTT
  • L = length of account key filter in bytes
  • T = type
0bLLLL0000
  • length = 0bLLLL = varies
  • type = 0b0000 = 0 = Account Key Filter
Mandatory
1 - s Account Key Filter varies Mandatory
s + 1 uint8 Field length and type
0bLLLLTTTT
  • L = length in bytes
  • T = type
0b00010001
  • length = 0b0001 = 1
  • type = 0b0001 = 1 = Salt
Optional (see Rotation)
s + 2 uint8 Salt varies Optional (see Rotation)
Account Key Filter

The advertised Account Key Filter allows a Seeker to quickly check whether a Provider might possess a certain account key (with a low false-positive probability, on average much less than 0.5%), before attempthing to connect. The seeker may also automatically connect and attempt to start the procedure when it sees a filter being broadcast that potentially contains one of its account keys, so as to reduce the rate of false positives further.

The Account Key Filter is a variable-length Bloom filter (example), constructed as follows:

  1. Let n be the number of account keys in the persisted Account Key list.
  2. Let s, the size of the filter in bytes, be (1.2*n + 3) truncated. For example, if 1 key is persisted, s = 4 bytes.
  3. Initialize the filter F as an array of s bytes, each set to 0.
  4. For each account key K in the persisted Account Key list:
    1. Produce a value V, where:
      1. The first 16 bytes are K
      2. The remaining bytes are the Salt (see Rotation).
    2. Hash V using SHA256, obtaining a 256-bit value H.
    3. Divide H into eight 32-bit unsigned integers, X = {X0, …, X7}.
    4. For each Xi:
      1. Let K be Xi modulo the number of bits in the filter, (s * 8).
      2. Get the byte in F at index (K / 8), rounded down.
      3. Within the byte, set the bit at index (K % 8) to 1.
      4. In other words:
        K = Xi % (s * 8)
        F[K/8] = F[K/8] | (1 << (K % 8))

Include the filter F as the Account Key Filter field, in the advertising data. Note that there is no "endianness" to this value, since there is no more or less significant byte—don’t alter the byte order.

Rotation

To reduce the ability to track devices by monitoring the Account Key Filter, the Account Key Filter shall be regenerated, with a new Salt, at least every 15 minutes while the device is actively advertising. However, note that if the device is connected over a BR/EDR link then the public address of one end of the link is being broadcast in the form of the channel access code, so privacy is limited.

There are two options for the salt:

Salt option A: BLE address

Salt with the current BLE address, also known as the random resolvable private address (RPA). If a callback is available when the RPA rotates, this is the simplest option, and is preferred.

To generate the Account Key Filter:

  1. Use the current 6-byte RPA (in big-endian, see Octet Order) as the Salt in step 4(a).
  2. In the advertised Fast Pair Account Data, include the generated filter in the Account Key Filter field. Do not include the optional Salt field.

Each time the RPA rotates, regenerate the Account Key Filter. Ideally, ensure that no advertisements are being broadcast using the new address and the old Account Key Filter. This can be done by pausing advertising briefly while the Account Key Filter is regenerated. However, as already noted above, there is a limit to the privacy that can be achieved with dual-mode devices with active BR/EDR connections.

Salt option B: Salt field

Use this option if there is no available callback when the BLE address rotates. Using a salt should be considered less secure than rotating with a BLE address, as tracking is still potentially possible if the two do not rotate at the same rate. The BLE address also introduces more entropy as it is longer. Prefer using the BLE address when possible.

To generate the Account Key Filter:

  1. Generate a random byte S.
  2. Use the byte S as the Salt in step 4(a).
  3. In the advertised Fast Pair Account Data, include the generated filter in the Account Key Filter field, and S in the Salt field.

Maintain a timer that fires once every 15 minutes. Each time the timer fires, regenerate the Account Key Filter. The timer can optionally fire faster, but no faster than once every minute.

GATT service

The Fast Pair Provider shall have the following GATT service.

Service UUID
Fast Pair Service 0xFE2C

This service shall have the following characteristics.

Fast Pair Service characteristic Encrypted Permissions UUID
Key-based Pairing No Write and notify 0x1234
Passkey No Write and notify 0x1235
Account Key No Write 0x1236

ATT MTU Size Negotiation

An MTU value of 83 should be used whenever possible, but the default value of 23 is allowed.

Characteristic: Key-based Pairing

This characteristic controls the Key-based Pairing procedure. In this procedure, a certain level of trust is established by verifying that the Seeker and Provider are both in possession of a pre-shared key. The key is different in each case:

  • Case 1: The pre-shared key is based on the anti-spoofing public/private key pair, and the Seeker’s own public/private key pair which will change for each pairing attempt.

    • The Provider is in pairing mode.
    • The Seeker verifies that the Provider is in possession of the anti-spoofing private key.

    Note that when in pairing mode, the Provider may also of course pair in the usual way, for example, to pair with a device that does not support Fast Pair’s Key-based Pairing.

  • Case 2: The pre-shared key is one of the account keys.

    • The Provider is usually not in pairing mode. (But this is not a requirement—The Provider should support using an account key even when in pairing mode.)
    • The Seeker and Provider each verify that the other is in possession of the account key.

Since both cases are extremely similar, except for which pre-shared key is used, they are combined in Procedure.

Data Format

See Procedure for how each format is used.

Octet Data type Description Value Mandatory?
0 - 15 uint128 Encrypted Request varies Mandatory
16 - 79 Public Key varies Optional

Table 1.1: Encrypted Request, written to the characteristic by the Seeker.

Octet Data type Description Value Mandatory?
0 uint8 Message type 0x00 = Key-based Pairing Request Mandatory
1 uint8 Flags
  • Bit 0 (MSB): 1 to request discoverability, 0 otherwise
  • Bit 1: 1 if the Seeker requests that the Provider shall initiate bonding, and this request contains the Seeker’s BR/EDR address. 0 otherwise.
  • Bits 2 - 7 are reserved for future use, and shall be ignored.
varies Mandatory
2 - 7 uint48 Either:
  • Provider’s current BLE address
  • Provider’s public address
varies Mandatory
8 - 13 uint48 Seeker’s BR/EDR Address varies Present only if Flags bit 1 is set
n - 15 Reserved (shall be ignored) varies Mandatory

Table 1.2: Raw Request. Decrypted from the Encrypted Request in Table 1.1.

Octet Data type Description Value
0 uint8 Message type 0x01 = Key-based Pairing Response
1 - 6 uint48 Provider’s public (BR/EDR) address varies
7 - 15 Random value (salt) varies

Table 1.3: Raw Response. Encrypted to produce the Encrypted Response in Table 1.4.

Octet Data type Description Value
0 -15 uint128 Encrypted Response varies

Table 1.4: Encrypted Response, sent by the Provider to the Seeker via a notify.

Procedure

Instead of immediately invoking any of the normal BR/EDR or BLE bonding procedures, the Seeker first enables Notifications on the Key-based Pairing characteristic, and then writes the data in Table 1.1 to it.

When handling a write request from a Fast Pair Seeker, the Fast Pair Provider shall do the following:

  1. If the optional Public Key field is present:
    1. If the device is not in pairing mode, ignore the write and exit.
    2. Otherwise:
      1. Use the received Public Key (a 64-byte point on the secp256r1 elliptic curve), the pre-installed Anti-Spoofing Private Key (also secp256r1), and the Elliptic-Curve Diffie-Hellman algorithm to generate a 256-bit AES key.
      2. Use SHA-256 to hash the 256-bit AES key.
      3. Take the first 128 bits of the result. This is the Anti-Spoofing AES Key, used in the next step.
  2. Using AES-128, attempt to decrypt the value. Since the value is a single 16-byte AES block, no IV or multi-block cipher mode is necessary.

    1. Which key to use:
      1. If an Anti-Spoofing AES Key was generated in step 1, use that key.
      2. Otherwise, try each key in the persisted Account Key List.
    2. If a key successfully decrypts the value, break, and continue to the next step.
    3. The value is decrypted successfully if the output matches the format in Table 1.2 (that is, if it contains either the Fast Pair Provider’s current BLE address, or the Fast Pair Provider’s public address).
    4. Note that at the end of the packet there is a salt attached. When possible, these salts should be tracked, and if the Provider receives a request containing an already used salt, the request should be ignored to prevent replay attacks.
    5. As an alternative to tracking salts, if the write includes the Provider’s private address, another way to prevent replay attacks is to bring forward the time of the next resolvable private address rotation so that the rotation occurs before the next Key-based Pairing write will be accepted.
  3. If no key could successfully decrypt the value, ignore the write and exit.

    1. Keep a count of these failures. When the failure count hits 10, fail all new requests immediately. Reset the failure count after 5 minutes, after power on, or after a success.
  4. Otherwise, save the successful key as K. Mark this K as usable for decrypting Passkey writes received on this LE link, but not Account Key writes nor any writes on any other link. Start a timer to discard K after 10 seconds if pairing has not been started (this may be the same timer as used in step 5 to enable temporary discoverability). Also discard K if this LE link disconnects.

  5. If the decrypted request’s Flags bit requesting discoverability is set to 1, become discoverable for 10 seconds, or until this pairing process completes, whichever comes first.

    1. Unlike other times when the Fast Pair Provider is discoverable, it shall not advertise Fast Pair Model ID data during this time.
    2. The reason this is needed: The Android versions Nougat and below will send a request with the discoverability bit set, to work around a bug in the OS where pairing with a device fails without first discovering the device.
  6. Produce the 16-byte Raw Response shown in Table 1.3, by concatenating the type and the Provider’s BR/EDR address, and then filling the remainder of the packet with a block of random bytes (that is, a salt).

  7. Encrypt the Raw Response with K to produce the 16-byte Encrypted Response shown in Table 1.4. Send this via a notify on the Key-based Pairing characteristic.

  8. If the Request’s Flags byte has bit 1 set to 1:

    1. This indicates that the Seeker is requesting the Provider to initiate bonding to the Seeker’s BR/EDR address, which is present in bytes 8-13.
    2. Send a pairing request to the Seeker’s BR/EDR address. The pairing request must be as described below (“During pairing” step).
    3. Reason this is needed: Having the Provider initiate works around an issue on some devices.
  9. If the Request’s Flags byte has bit 1 set to 0:

    1. Wait up to 10 seconds for a pairing request. If none is received, exit.
    2. Note that this might be a BR/EDR request, from a different address (the Seeker’s public address, instead of its resolvable private address). We will re-verify during pairing that the requesting device is in possession of K.
    3. Even if it’s a BLE pairing request, the same procedure shall be followed, to prevent man-in-the-middle. After pairing over BLE, the devices should use cross-transport key generation to pair over BR/EDR.
  10. During pairing:

    1. When a pairing request/response packet is received from the Seeker: If the Device Capabilities in the request are NoInput/NoOutput, end pairing, to avoid using the Just Works pairing method.
    2. For the pairing request/response packet sent by the Provider: Set the Device Capabilities field to Display/YesNo. This triggers the Numeric Comparison pairing method (also known as Passkey Confirmation on Android). We rely on this to confirm that the requesting device is in fact the Fast Pair Seeker, and that there is no man-in-the-middle. See examples.
    3. Reason this is needed: The Out-of-Band pairing method would be a better fit, but the platform does not expose it on all desired versions of Android.
  11. When confirmation of the passkey is needed, wait up to 10 seconds for a write to the Passkey characteristic.

    1. Normally, with this pairing method, the user would confirm that the passkeys displayed on each device’s screen are identical. Instead, only for this pairing, we transfer them over BLE, encrypted with the trusted pre-shared key.
    2. Note that this approach should not be taken for devices that have a screen or a keyboard as it makes a slight compromise on MITM protection. Fast Pair currently does not support those device types yet because of this.
    3. If the 10 second timer expires with a passkey being written, then discard K.
  12. When a value is written to the Passkey characteristic, this is the Encrypted Passkey Block. Decrypt it with K to yield a Raw Passkey Block, with format shown in Characteristic: Passkey > Table 2.2 (type = Seeker’s Passkey).

  13. If decryption fails, ignore the write and discard K.

  14. Otherwise, the Raw Passkey Block contains a 6-digit passkey PSeeker, which is the passkey that the Seeker expects.

  15. Compare PSeeker with our own expected passkey, PProvider.

    1. If the values are equal, reply “yes” to the confirmation.
    2. Otherwise, reply “no” to the confirmation, causing pairing to fail.
  16. Regardless of whether pairing failed, produce another Raw Passkey Block, with format shown in Characteristic: Passkey > Table 2.2, containing our own expected passkey, PProvider.

    1. Ensure the block has the correct type (Provider’s Passkey; see table).
    2. Note: Do not reuse the salt from the Passkey Block received from the Seeker. Generate a new random value.
  17. Encrypt the Raw Passkey Block with K, and send the resulting Encrypted Passkey Block via a notify on the Passkey characteristic.

  18. If the Seeker receives and decrypts the correct passkey P, the Seeker will also reply “yes” to the confirmation, and pairing will succeed.

    1. If the pairing succeeds, then mark K as usable for decrypting Account Key writes on this LE link, but not for any subsequent Passkey writes nor any writes on any other link. Start a timer to discard K after 10 seconds. Also discard K following any attempt to write an Account Key and, as per step 4, if the LE link disconnects.
    2. If the pairing fails, discard K.
  19. Switch the device capabilities field back to NoInput/NoOutput so that new pairings continue as expected.

Examples
Example 1: Successful pairing attempt (no man-in-the-middle).
Example 2: Failed pairing attempt, with a man-in-the-middle.

Characteristic: Passkey

This characteristic is used during the Key-based Pairing procedure.

Octet Data type Description Value
0 - 15 uint128 Encrypted passkey block varies

Table 2.1: Encrypted Passkey Block. See Key-based Pairing procedure for usage.

Octet Data type Description Value
0 uint8 Message type One of:
  • 0x02 = Seeker’s Passkey
  • 0x03 = Provider’s Passkey
1 - 3 unit32 6-digit passkey varies
4 - 15 Random value (salt) varies

Table 2.2: Raw Passkey Block. Decrypted version of Table 2.1.

Characteristic: Account Key

After pairing, the Fast Pair Seeker will write an Account Key to the Fast Pair Provider.

Octet Data type Description Value
0 - 15 uint128 Account key (encrypted) varies

Upon getting a write request, the Fast Pair Provider shall do the following:

  1. Decrypt the account key using the shared secret generated from step 4 in the above procedure.
    1. Before decrypting, verify that the shared secret was used to decrypt the passkey request from step 12. If this step has not passed using this secret, ignore this write and quit.
    2. At this point, the shared secret (K in the procedure above) will not be used again for this pairing. Any requests which come in encrypted with this key without restarting the procedure above should be rejected.
  2. Verify that the decrypted value starts with 0x04. If it does not, ignore this write and quit.
  3. Check whether the persisted Account Key list has space for the new value.
  4. If not, delete the least recently used value from the list.
  5. Add the new value to the list.

Account Keys in the list are used during Key-based Pairing.

Appendix

Test Cases

Provided below are tests that a provider’s implementation can run against to verify that many of the pieces are working as expected (specifically, there are cases for hashing, crypto and the bloom filter). Once these underlying tests are passing, implementation of the requests and responses in the procedure outlined above should be more straightforward.

SHA-256 Hashing

Input value:

0x11, 0x22, 0x33, 0x44, 0x55, 0x66

Hashed result:

0xBB, 0x00, 0x0D, 0xDD, 0x92, 0xA0, 0xA2, 0xA3, 0x46, 0xF0, 0xB5, 0x31,
0xF2, 0x78, 0xAF, 0x06, 0xE3, 0x70, 0xF8, 0x69, 0x32, 0xCC, 0xAF, 0xCC,
0xC8, 0x92, 0xD6, 0x8D, 0x35, 0x0F, 0x80, 0xF8

AES Encryption

Input value:

0xF3, 0x0F, 0x4E, 0x78, 0x6C, 0x59, 0xA7, 0xBB, 0xF3, 0x87, 0x3B, 0x5A,
0x49, 0xBA, 0x97, 0xEA

Secret key:

0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6, 0xCF, 0x5E, 0x3F, 0x45,
0x61, 0xC3, 0x32, 0x1D

Encrypted output:

0xAC, 0x9A, 0x16, 0xF0, 0x95, 0x3A, 0x3F, 0x22, 0x3D, 0xD1, 0x0C, 0xF5,
0x36, 0xE0, 0x9E, 0x9C

ECDH Key Exchange

Bob’s private key:

0x02, 0xB4, 0x37, 0xB0, 0xED, 0xD6, 0xBB, 0xD4, 0x29, 0x06, 0x4A, 0x4E,
0x52, 0x9F, 0xCB, 0xF1, 0xC4, 0x8D, 0x0D, 0x62, 0x49, 0x24, 0xD5, 0x92,
0x27, 0x4B, 0x7E, 0xD8, 0x11, 0x93, 0xD7, 0x63

Bob’s public key:

0xF7, 0xD4, 0x96, 0xA6, 0x2E, 0xCA, 0x41, 0x63, 0x51, 0x54, 0x0A, 0xA3,
0x43, 0xBC, 0x69, 0x0A, 0x61, 0x09, 0xF5, 0x51, 0x50, 0x06, 0x66, 0xB8,
0x3B, 0x12, 0x51, 0xFB, 0x84, 0xFA, 0x28, 0x60, 0x79, 0x5E, 0xBD, 0x63,
0xD3, 0xB8, 0x83, 0x6F, 0x44, 0xA9, 0xA3, 0xE2, 0x8B, 0xB3, 0x40, 0x17,
0xE0, 0x15, 0xF5, 0x97, 0x93, 0x05, 0xD8, 0x49, 0xFD, 0xF8, 0xDE, 0x10,
0x12, 0x3B, 0x61, 0xD2

Alice’s private key:

0xD7, 0x5E, 0x54, 0xC7, 0x7D, 0x76, 0x24, 0x89, 0xE5, 0x7C, 0xFA, 0x92,
0x37, 0x43, 0xF1, 0x67, 0x77, 0xA4, 0x28, 0x3D, 0x99, 0x80, 0x0B, 0xAC,
0x55, 0x58, 0x48, 0x38, 0x93, 0xE5, 0xB0, 0x6D

Alice’s public key:

0x36, 0xAC, 0x68, 0x2C, 0x50, 0x82, 0x15, 0x66, 0x8F, 0xBE, 0xFE, 0x24,
0x7D, 0x01, 0xD5, 0xEB, 0x96, 0xE6, 0x31, 0x8E, 0x85, 0x5B, 0x2D, 0x64,
0xB5, 0x19, 0x5D, 0x38, 0xEE, 0x7E, 0x37, 0xBE, 0x18, 0x38, 0xC0, 0xB9,
0x48, 0xC3, 0xF7, 0x55, 0x20, 0xE0, 0x7E, 0x70, 0xF0, 0x72, 0x91, 0x41,
0x9A, 0xCE, 0x2D, 0x28, 0x14, 0x3C, 0x5A, 0xDB, 0x2D, 0xBD, 0x98, 0xEE,
0x3C, 0x8E, 0x4F, 0xBF

Generated shared key (either Alice’s public + Bob’s private or Bob’s public + Alice’s private):

0x9D, 0xAD, 0xE4, 0xF8, 0x6A, 0xC3, 0x48, 0x8B, 0xBA, 0xC2, 0xAC, 0x34,
0xB5, 0xFE, 0x68, 0xA0, 0xEE, 0x5A, 0x67, 0x06, 0xF5, 0x43, 0xD9, 0x06,
0x1A, 0xD5, 0x78, 0x89, 0x49, 0x8A, 0xE6, 0xBA

AES Key from ECDH Shared Secret

This is step 1b in the procedure outlined above.

ECDH shared key:

0x9D, 0xAD, 0xE4, 0xF8, 0x6A, 0xC3, 0x48, 0x8B, 0xBA, 0xC2, 0xAC, 0x34,
0xB5, 0xFE, 0x68, 0xA0, 0xEE, 0x5A, 0x67, 0x06, 0xF5, 0x43, 0xD9, 0x06,
0x1A, 0xD5, 0x78, 0x89, 0x49, 0x8A, 0xE6, 0xBA

Generated AES key:

0xB0, 0x7F, 0x1F, 0x17, 0xC2, 0x36, 0xCB, 0xD3, 0x35, 0x23, 0xC5, 0x15,
0xF3, 0x50, 0xAE, 0x57

Bloom Filter

Provider’s BLE address:

00:E0:4C:87:63:99

First account key added to the filter (combine with the above BLE address):

0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB,
0xCC, 0xDD, 0xEE, 0xFF

Resulting bloom filter:

0x50, 0x60, 0x18, 0x30

Second account key added to the filter (combine with the above BLE address):

0x11, 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66,
0x77, 0x77, 0x88, 0x88

Resulting bloom filter from both added account keys:

0x76, 0x15, 0x00, 0x78, 0x10