One Time Passwords are everywhere these days. We have great standards, like
HOTP and TOTP, which means that all our implementations can be
interoperable. We also have open source implementations of these standards,
such as FreeOTP. Tons of server applications and web services use OTPs every
day for millions of users.
But, currently, one big user experience issue comes to the fore: typing OTP
codes is error-prone and time-consuming. Can’t we do better?
On and off for the last few years, I have been working to improve this
situation. The result I have come up with is a companion app to FreeOTP called
Jelling that allows FreeOTP to send OTP codes directly to paired Bluetooth Low
Energy devices. I hope to explain in this post how Jelling works and outline
the challenges that still remain. Hopefully you, my most esteemed reader, can
help us close some of the gaps that remain.
Bluetooth LE GATT
Bluetooth Low Energy (BLE) has a mode of operation called the Generic Attribute
Profile (GATT). Applications define collections of services that contain
characteristics which are manipulated by remote entities.1 This means we can
define our own protocol using GATT and use this protocol to send a token code
from a Sender to the intended Receiver. The Sender will be an Android or iOS
version of the FreeOTP application. The Receiver will usually be a Windows,
macOS or Linux computer.
First, we have to decide whether the Sender or the Receiver will be the
Peripheral or the Central. A Peripheral sends advertisement packets which tell
other devices that the specified service is available for use. The Central
scans for these advertisements and, when found, can connect to the Peripheral
and use its Services. Two principles are important. First, a Peripheral can
only have one Central, but a Central can have many Peripherals. Second,
scanning uses more power than advertising, so it should be used sparingly.
Second, we need to consider the user experience. Attempting to initialize the
token sharing transaction from the Receiver might seem to make the most sense,
since the user is already typically doing something on the Receiver when he
wants a token code. However, this means we would need to negotiate over BLE
about which token code to receive. This negotiation would use a lot of power.
Further, because the tokens are authentication credentials, we must confirm
on the Sender before sending them to protect from token theft. On the other
hand, if the Sender selects the token first and initiates the share we don’t
require negotiation or confirmation at all. Therefore, we can reduce the number
of GATT requests we need to send to one.2
Third, we need to consider security. We can’t just share a token without being
sure about who will receive it. This means that we must use both BLE
authentication and encryption. Further, if we implemented the Sender as a
Peripheral where it sent characteristic notifications including the token code,
we often can’t see who is subscribed to those notifications due to platform API
limitations. This doesn’t work for our case since we might care that some OTP
codes are only sent to some paired devices.
All of this leads to a simple implementation. The Sender (FreeOTP) operates in
Central mode and the Receiver (computer) is a Peripheral that exposes a single
B670003C-0079-465C-9BA7-6C0539CCD67F) which, in turn, exposes a
single, write-only characteristic (
protect this characteristic using both BLE encryption and authentication. The
Sender initiates the connection and transfers the OTP by sending a single write
to the characteristic containing the OTP code it wishes to share. This means
that the Receiver needs to advertise whenever it wants to receive token codes
(default: always3). The Sender scans and connects only when it wants to
share; this preserves battery life. The Sender chooses which token to share and
which Receiver to send it to. Once the Receiver has the token, it emulates a
keyboard and types wherever the cursor is. This user experience is simple and
The Good News
I’ve implemented Receivers for Windows, macOS and Linux. Each is
implemented using its platform’s native development tooling and languages (C#,
Swift and C, respectively). The Receivers advertise themselves as a BLE
Peripheral which FreeOTP can connect to via pairing. Once paired, FreeOTP can
send tokens to the selected Receiver.
I’ve also implemented this behavior in FreeOTP. For iOS, this is already
merged to master. For Android, this lives in the bt branch which I hope to
merge soon. Like always, you can click a token to show the code directly.
However, now you can choose to share the token. This pops up a secondary menu
which shows the available Receivers within range. Selecting a Receiver shares
the token if already paired. Otherwise, it begins the pairing procedure.
The Bad News
Unfortunately, the platform support for this functionality appears to be
frustratingly inconsistent. The following chart documents my success and
failure. This test reflects my tests with both FreeOTP and LightBlue (a BLE
GATT testing application on iOS and Android). This proves (to me) that the
problem isn’t my code but rather platform incompatibilities. For more details,
see the charts in the
README.md of each repository.
|Pixel||Nexus 5x||iPhone 6+|
Sharing works with my iPhone 6+. It does not work from either my Google Pixel
or Nexus 5x. I suspect my Pixel has problems because I wasn’t able to get it to
work anywhere. However, on my Nexus 5x the simple, single GATT write never
causes the callback to fire in my Windows code. It does work if I disable
authentication. This leads me to believe that there is a compatibility issue
between Windows and Android during pairing.
The macOS BLE implementation seems to be the most mature of all the platforms.
Sharing works on both my iPhone 6 and Nexus 5x. Like I mentioned above, my
Pixel seems horribly broken. I even updated to 8.1 beta to see if it has been
fixed. No dice. I will try on another Pixel that has been reset to factory
Unfortunately, Linux appears to be the most broken of all the platforms. With
my iPhone 6+ I am able to successfully see the advertisement and perform
service discovery. But when I perform a GATT write, pairing never begins. My
Nexus 5x can see the advertisement and connect, but it fails performing service
discovery. My Pixel connects only in non-BLE mode (it does seem to connect in
BR/ATT mode; but that is useless).
The Call for Help!
I’d really like to bring this functionality to a FreeOTP release in the near
future. But we need your help! “How?” you ask.
Assist debugging combinations where things are broken. In particular, we
need help from Bluez. If you are a Bluez developer, please contact me!
Review the code. I’m not a Windows or macOS developer. If I’m doing
something wrong, it is probably my fault. Bug reports are welcome. Patches
are even better.
- Search for “Bluetooth GATT” to learn more.
- “Here is the token code.”
- There is a trade-off here between usability, privacy and battery usage. The later is much less of a concern in the Receiver since it has a large battery and is often plugged in. Privacy is a tough one. If you’re advertising you can be used by FreeOTP, you can also be tracked by someone physically nearby. We consider this low-risk.
Source From: fedoraplanet.org.
Original article title: Nathaniel McCallum: Sending FreeOTP Codes Over Bluetooth.
This full article can be read at: Nathaniel McCallum: Sending FreeOTP Codes Over Bluetooth.