Biometric Password Manager (5 Part Series)
Password prompts. Password prompts everywhere. Between logging into my work VPN every day and sudo access on my personal machine, I have to enter multiple passwords multiple times every day. I'm a lousy typist, which means I'll often mistype the passwords in question (especially on those godawful butterfly keyboards) and frustrate myself to no end.
Compare that with my phone, where in most cases I can use my fingerprint to log in and even authorise transactions. Entering passwords or PINs is a back-up option rather than the default. Convenient and secure authentication, based on something you are rather than something you know. Wouldn't it be brilliant if I could do this on every device I use?
Yes, yes it would be. And here's how I want to do it.
I am taking a more design-led approach in this section, where I outline what task I want to achieve and then flesh out how I want to achieve it. And what I want can be summarised in a sentence:
I want to be able to enter my passwords on any computer with a touch of my finger, but still be secure.
I decided that this can be broken up into three distinct sub-goals:
- Security: The password should only be unlocked for the right person. Someone who got hold of the device should not be able to retrieve the passwords without significant effort.
- Compatibility: The device should be able to enter passwords on any reasonable computer without special software or drivers.
- Simplicity: Ideally I'd only store a handful of passwords on the device, I want to be able to quickly scroll through the list and confirm my fingerprint to enter the password.
I will discuss the implementation details in more depth later, but for now I would like to draw your attention to the inherent conflict between the security goal and the usability goals (2 and 3). This is the inherent challenge in designing security systems, as increasing a thing's usability often also increases its attack surface. I think the biometrics approach is better than secure entry keypads or miniature card readers such as the Mooltipass for this tradeoff - a secure keypad still requires you to enter a code which can be guessed, and the Mooltipass cards can be lost. A fingerprint-based system provides a reasonable level of security and convenience - a third party can't get to your passwords without cracking open the device and spending some time with a debug probe, and if you lose your finger you probably have bigger immediate problems than keeping your data secure.
The key reason for doing this project is that I'm tired of mistyping my password which we also have to change every three months. My ideal device would be something that sent that particular password down USB with a simple button press, however that's not super secure! As mentioned above I think fingerprint authentication is ultimately more secure in the average case than a typed-in password, because once you're verifying who you are rather than what you know, you can make the "password" as long and secure as you can because no human will ever see it. It also makes sense to go with a keychain approach for storing a handful of passwords.
The other big reason for me starting this is that I want to learn something new. To that end I decided that I want to go back to ARM microcontrollers which I know and hate from university, but on slightly different terms this time - using Rust instead of C/C++, and using a microcontroller much more popular in the maker circles. I settled on the STM32 family, and more concretely on the STM32F446RE part simply because there's a development board for it that costs £13 and it's a powerful little chip.
Given the overall goals of this project, I decided on a USB dongle form factor. With USB, I can register the device as a keyboard and send the password keystroke by keystroke to the host. USB keyboards work on any system that has USB - so no extra drivers are necessary. The configuration interface can be a virtual serial port, which also should not require extra drivers. Finally, USB can be used for firmware updates.
I decided to use a scroll wheel control to select the password to enter. The scroll wheel should have nice detents and should be able to rotate freely. I want the interface to scale well to 10s of passwords stored, and so buttons are right out. Notably, the Mooltipass also uses this approach.
An OLED display will be used to show the user which password they have selected, as well as other information such as the status of the device if in configuration or DFU modes. I am also considering adding a Neopixel-type RGB LED for extra indication - perhaps I'll want to let the user associate each password with a colour.
Further on the user interaction - because the fingerprint reader I have in mind for this can also act as a button, I decided that placing one's finger on the reader will confirm the selection and acquire the fingerprint and try to enter the password. A fingerprint confirmation will also be necessary for entering configuration mode and updating firmware. For extra security, I may add a feature which erases the password list if no successful authentication is made a certain number of times, with exponential backoff to prevent the user accidentally erasing their device.
As for the encryption - I will need to come up with a reasonable scheme that would require an attacker to have physical possession of the device and at least some lab equipment to recover the password list. At the moment, I am thinking of combining the main microcontroller's unique ID and the user's fingerprint template as a symmetric encryption key.
I would have liked the fingerprint module itself to be able to generate a cryptographic value based on the user's fingerprint and then only release it if the authorised finger was detected - however this functionality is not available on the module I have chosen.
While I mostly chose Rust because I wanted to learn the language, I also know that it is a much safer language than C/C++ when it comes to memory access. Unless code is explicitly marked as unsafe, Rust will attempt to validate memory access at compile time, catching entire classes of bugs such as use-after-free or buffer overflows.
I chose this particular STM32 part for several reasons. One, the cheap development board - the chip itself is about $6 so I would not be surprised if ST sold the things at cost price. Two, it's a pretty powerful part. And three, the STM32 family is popular with makers and hobbyist electronics people. As a consequence, there is lots of documentation, both from the vendor and the community. There are also Rust libraries for accessing the chip's peripherals in a relatively nice way.
The fingerprint sensor was the hardest thing to find. While there are some Arduino-friendly devices sold by Adafruit (with clones from the usual suspects), they are rather big. Good for safes or doors, not good for portable devices. It did take some research time to find a part which was small and had a reasonably convenient interface. Predictably, it comes from a relatively unknown Chinese supplier, with many clones on the market. It is the Grow R502, a module roughly the size of a 10p coin, with a UART interface. It can be ordered through AliExpress or Alibaba and comes relatively quickly. However, documentation in English is a bit lacking, and I could not find demo software to go with the module. I have emailed the vendor.
The Grow R502 does not do everything I'd like it to do. For one, I'd love a single piece of functionality to be added to it: when the fingerprint is first enrolled, a token is generated, which is then only returned on successful matches against that fingerprint. This would simplify my design and make the whole system more secure, as I could use that as the encryption key. Bit of trivia: the fingerprint module has a GD32 MCU, an STM32 clone from GigaDevice.
Given the features I want to include on the device and the constraints imposed by the parts I want to use, I decided to lay out the system like this:
I will blog my way through this project, and possibly even make some demonstration videos along the way. I want to share notes from the development process with you, and I want to get some feedback on my plans. For now I am planning 11 blog posts.
- Rust on the STM32F446RE Nucleo board - where I skip the blinky and go straight for getting something on the OLED display. I will also try to cover initialisation code generation in STM Cube IDE and whether it's a good idea to do first before translating into Rust.
- R502 exploration - where I try to find some existing software to test the module with and start writing a test STM32 program in Rust.
- USB keyboard - where I try to create a simple USB Rubber Ducky clone from SPI flash
- Basic prototype - where I build a device which sends one password stored in plain text when the fingerprint is correct
- Configuring passwords - where I create a composite USB device which has both a USB keyboard and a virtual serial port, and then use that to configure the password list
- UI prototype - where I connect the encoder and use it to navigate up and down the list of passwords, and talk a bit more about the overall UX of the thing
- Encryption - where I start integrating encryption routines into the firmware
- Full breadboard prototype - where I put everything together on a breadboard and talk through what's going on
- Electronic engineering - where I create a full schematic and design a PCB for the USB dongle
- The one where I put together the device on the PCB and hope that it works
- Reflections - where I spend some time talking about what I learned through this project and where I want to take it; with some usage commentary if the thing even works after I put it together
There's a lot on here and I hope to be able to do it all in reasonable time. For now I'd like to invite you all to follow this series and send in some comments on this project. Until next time!