Can you uniquely identify a BLE MAC address in Android 5.0? - android

In Android 5.0, BLE doesn't use static MAC addresses anymore, but the MACs change after intervals, using a method called IRK and public key cryptography.
Is it possible to uniquely identify an Android phone in a way that can not be spoofed by the user of that phone, or has that become completely impossible now unless the other side co-operates?

No, it is not possible. That's the whole idea of using IRKs: only bonded devices can identify it after the address changes. For those who do not possess the IRK (because they have not bonded), it would always appear as a completely new device (hence the privacy).

The private address is generated using the devices IRK exchanged during the previous pairing/bonding procedure. So if they have not bonded, you can't get irk.

Related

Is there a way to identify a BLE device in both Android and iOS

I knew how to get the mac address of a BLE device in Android by calling address from BleDevice
bleDevice.address
But I didn't find a way to get the UUID of the device
On the other hand in iOS I can get the UUID of a device by calling identifier from CBPeripheral peripheral.identifier
But I didn't find a way to get the mac address.
Note that the mac address is not advertised in the advertisement data in iOS those are only the fields in advertisement data
kCBAdvDataIsConnectable
kCBAdvDataServiceUUIDs
kCBAdvDataTxPowerLevel
kCBAdvDataRxPrimaryPHY
kCBAdvDataLocalName
kCBAdvDataRxSecondaryPHY
kCBAdvDataTimestamp
My problem is I need an identifier that I can use in both Android and iOS to identify my ble device
In Android, BLE devices are distinguished by their hardware address. CBPeripheral.identifier property seems to be for iOS only and how their peripheral manager identifies and assigns the devices. Probably your implementation should use String as common denominator, for iOS should return identifier and for Android bleDevice.address.
Indeed the iOS MAC-> UUID Issue is a long standing one (and with search you'll see a number of threads on the issue).
Unfortunately there's no simple good news for a solution, and only some potential solutions... here's a summary and hope one of the paths may work for you:
iOS hides the MAC address and randomly generates a UUID for you. You can store the UUID on the phone, but it will be unique to that phone. If we both have iPhones and scan the same peripheral, we'll see different UUIDs. iOS generates the UUID on the device and hides the MAC address.
Indeed the iOS MAC-> UUID Issue is a long standing one (and with search you'll see a number of threads on the issue).
To make life easier, some devices add the some or all of the MAC address in their secondary advertising packet or in a unique device identifier in the Manufacturer data of the device and read this from the advertisement in iOS.... If you're in control of the devices firmware then I'd add this... of course if this is another party's device then you're at the mercy of their implementation.
Another possible route is using the GATT Device Information Service (Service UUID: 0x180a). Under this service there is a Serial Number String (Assigned UUID: 0x2A25) characteristic which can hold a serial number specific to the Bluetooth low energy device. But note, This is optional though and not all BLE profiles will have it, and you have to connect to the device first to retrieve this information which may not fit your usage pattern.
There may be other unique proprietary identifers for a specific device that are exposed either in advertising data or characteristics
If you're talking about an iBeacon then the advertised ibeacon guid and bluetooth address are effectively the same value.
In cases where I'm implementing the firmware for a custom BLE device I always add the 6 byte MAC address to the advertising manufacturer data. Some devices do this (or some other identifier that's not as unique like "Thermometer1020" with the 1020 hopefully being unique). But again you're at the mercy of what the device is advertising.
If you're looking for a solution that works across a wide variety of BLE devices and vendors, then unfortunatly Apple has made life difficult, if not impossible for you in their quest for security, privacy and limiting use cases of connected device applications.

How to work around Android rotating the BLE Mac Address after every connection?

I'm working on a project where a central device connects to peripherals advertising a specific GATT service. The central device exchanges some information with the phone after which the connection is no longer needed and the device disconnects.
The problem that I'm running into is that the phone seems to change its MAC address after every new connection. So basically, every time I connect to a device it looks like a new device appears. So my central node tries to connect to the new service again. This is very annoying since I was thinking of using the MAC address to know that I had already connected to the device and did not need to do the information exchange again. Note that I know that the MAC address rotates every 30 minutes anyways and that's something I'm willing to deal with, connecting once every 30 minutes is fine, but the rotation on connection causes my device to chain connect to the phone that always appear like a new device.
A few ideas I have thought of to work around this:
Obtain the new MAC address from the phone and share it with the central device. That has 2 issues:
MAC Address is not accessible with the Android APIs except using sketchy reflection.
It would only work if a single central device is in Range, since only the last node would know the new MAC address to avoid.
Advertise service Data with a random ID that I would manually rotate every 30 minutes. This data could be in the same advertisement packet. That allows me to ID the device without compromising privacy.
It however might be very hard to implement on iOS that has very restrictive background advertisement APIs. iOS doesn't actually appear to have the same behavior though so it might be possible to implement a completely different method for iOS.
Disabling this "rotate MAC on connection" feature doesn't seem possible.
How can I work around this issue?
If you Bluetooth pair the devices, then the Android device will send over its IRK (Identity Resolving Key). With this you will be able to derive if a given Bluetooth Device Address was generated by the particular IRK or not. That way you can identify an Android device. It works the same if you replace Android with iOS.
See your Bluetooth stack's documentation how to deal with IRKs.

Android save BLE device to reconnect after app close

I'm developing an android BLE app in which i try to establish a stable Connection between the app and a BLE device.
To achieve this i want my app to keep the BLE device data in shared preferences or somewhere.
After app close, i'd like my app to retrive this data and attemp a connection without scan for devices.
I prefer to avoid scanning every time becouse scan gives me a lot of problems and it takes time.
How can do it? Is there a way to save BLE data?
You need to store the Bluetooth Device Address (AB:CD:EF:01:02:03) of the device in for example shared preferences or in a sqlite db. Then use https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getRemoteDevice(java.lang.String) when you later restart the app to retrieve a BluetoothDevice object. Once you have the BluetoothDevice object you can use the connectGatt method as usual.
There are some undocumented things you need to keep in mind however. Due to some horrible design flaws in Android's BLE API there is no way to tell it if you mean the given address is a Public Address or a Random Address. (You can read more about different address types at https://devzone.nordicsemi.com/question/43670/how-to-distinguish-between-random-and-public-gap-addresses/). The getRemoteDevice method should take an additional parameter "random address/public address" but it doesn't. Without the correct address type, the Bluetooth controller cannot connect to the device.
Android's BLE stack has some internal heuristics to "guess" if the address is public or random, but unfortunately that differs between Android versions and also if you use autoConnect=true or false. However if you have bonded the device (https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()) then it will store in its internal DB whether the given address is public or random. If the peripheral you want to connect to uses a random resolvable address it can handle that as well if you use bonding. Therefore I strongly suggest to use bonding.
If you don't use bonding you will need to scan before you connect to the device, since when you start a scan and a device is detected, Android's BLE stack will temporarily (until next restart of Bluetooth) remember the address type for an address.
I will continue on Emil's answer.
Thanks #Emil for the 'big picture'.
Google API is flawed and you can't use getRemoteDevice() because it only understand 48bit address and do not know about all BLE address types.
You can't use Bonding if the device is dumb (anyone would be able to bond to it).
So your only solution is a Bluetooth scan.
However, since Android6 (Marshmallow) the user has to give location permission to apps using Bluetooth scanning. If you don't permit, it won't work.
So if you buy a Bluetooth thermometer (or heart-rate monitor or whatever), you have to permit the app to access location of your phone (or tablet).
That's absurd!
#Emil call it a 'Google flaw'. You may also call it a 'Google machination'.
If I was a thermometer designer, I would add a push-button to accept bonding. It seems ridiculous because the button will only be used once.
[1]: https://www.polidea.com/blog/a-curious-relationship-android-ble-and-location/

Platform-agnostic bluetooth device identification mechanism

I'm trying to write a cross-platform abstraction over Bluetooth. When it comes to identifying devices (or peripherals, as iOS calls them), I'm a bit stumped.
iOS includes an identifier property, which is a generated ID assigned to a device the first time it connects. However, Android does not seem to have an equivalent concept. The best I seem to be able to do is to use BluetoothDevice.getAddress, which returns a MAC address. I can then turn that into a UUID by doing some byte twiddling.
Is there anything better I can do here to uniquely identify devices in an agnostic manner?
As you already noticed, you cannot use the identifier property to identify a device on iOS.
The correct way to do this is to advertise a service with a characteristic's value you can control on device A and discover this on the device B.
Depending on what you want to achieve, there are also SDKs you can use for device discovery, for example newaer, p2pkit or google nearby just to name a few.
Disclaimer: I work for Uepaa, developing p2pkit.io for Android and iOS.

Using android MAC address as unique identifier

I have been reading a number of posts here on stackoverflow about android not having unique identifiers. But could I not use the MAC address of the device as a unique id? If not, why not? If yes, does anyone know how to get it programmatically (i.e. a piece of code [please])?
FYI:
My app requires network so devices without networks won't be able to install it.
But could I not use the MAC address of the device as a unique id?
Not 100% reliably.
If not, why not?
Here is what Google has to say on the subject:
It may be possible to retrieve a Mac address from a device’s WiFi or Bluetooth hardware. We do not recommend using this as a unique identifier. To start with, not all devices have WiFi. Also, if the WiFi is not turned on, the hardware may not report the Mac address.
Also, you are assuming that all devices will accurately report the MAC address by one means or another. I would assume that custom ROMs, and perhaps rooted devices, might be able to spoof the MAC address. This may or may not be a concern for you -- I'm just pointing it out.
My app requires network
At best, it can require WiFi. It cannot require that WiFi is turned on.
does anyone know how to get it programmatically (i.e. a piece of code [please])?
See this answer.
MAC is unique, but can be spoofed by the user to any value, which would make it very insecure. If that's not a problem, you're probably good to go !

Categories

Resources