esp32-mfa-totp-generator icon indicating copy to clipboard operation
esp32-mfa-totp-generator copied to clipboard

ESP32 MFA TOTP Generator

ESP32 MFA TOTP Generator

Build with PlatformIO CI

Buy Me A Coffee

This is a personal project that creates MFA TOTP codes. I created it to help me to get TOTPs without interacting with my phone. Before creating it, every time I needed a new TOTP, I had to:

  • find my phone
  • if it is not charged, charge it, and then wait a few minutes until it is fully operational
  • if it is charged, unlock it
  • find a Virtual MFA App
  • unlock it
  • find the TOTP for the service I need, which sometimes requires a lot of vertical scrolling.

Therefore, my plan is to place this MFA device under my monitor, and just look at it when I need to get a new TOTP, instead of doing the aforementioned process. It is going to generate TOTPs for the services I use the most, and have short non configurable session times.

Other motives:

  • when using my personal phone to get mfa totp codes, I get distracted by notifications and a lot of other things.
  • not safe to store secrets on a device that you use when going out. If the device is lost, and there is no security layer to protect unintended access to the virtual MFA App, there is a high chance someone can impersonate your identity.
  • secrets are decoupled from the device, and this allows me to easily switch to another one when necessary. I just need to insert the SD card containing my services onto another device flashed with the same code.
  • not all services that I use support fido keys or passkeys.
  • I wanted to learn how MFA works.
  • I wanted to learn how ESP32 works.

https://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/166f6ea7-1046-4117-ae22-67991c8e6d8c

https://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/6e240518-a35b-4bf0-8a41-ece0dad9efb9

Parts

  • ESP32-2432S028
  • 3D printed black case
  • Acrillic case

INFO: you can buy this board from Aliexpress clicking on any of these affiliate links: USD BRL

INFO: you can buy this acrillic case from Aliexpress clicking on any of these affiliate links: USD BRL

INFO the 3D model for the black case was taking from this link

Total Project Cost

Part Cost
ESP32-2432S028 9.25 USD
3D printed black case 12.7 USD
Acrillic case 2.5 USD

INFO: all prices do not consider expenses with taxes and shipping.

INFO: all prices were taking in February 2024.

Dev Environment Requirements

INFO: Node.js is required because it runs several development tools used in this project. Among these tools are those that enforce the "conventional commits" standard. This standard is a lightweight convention on top of commit messages, offering an easy set of rules for creating an explicit commit history.

INFO: if platform.io extension does not recognize your board after clicking on Upload, Upload and Monitor or Monitor buttons, it means the driver was not properly setup. In Macos, after installing the driver from Sillicon Labs, I had to restart the system before mac could identify the board.

Runtime Requirements

  • 2.4Ghz WiFi signal with internet connection, in order to sync with NTP
  • SD card

Pre-build Steps

Before building the code, set the following env variables:

export WIFI_SSID=CHOCOLATE
export WIFI_PASSWORD=CHOCOLATE

This step is required because the board uses the NTP server to set its time.

WARNING: remember to use a network which has access to the internet, and is isolated from your main network.

WARNING: platform.io vscode extension tasks (build, upload, monitor...) are not using env variables. Therefore, you must open platformio.ini and set -D WIFI_SSID and -D WIFI_PASSWORD with your values.

Project Setup

  1. Install PlatformIO IDE extension in VSCode.
  2. Open this project in a new vscode workspace, and wait for Platform.IO to install everything.
  3. Connect your board to your computer. If you installed the proper drivers, the next steps should work just fine.
  4. Click on the Platform.IO button, in VSCode's sidebar.
  5. Then click on esp32-2432S028Rv3 -> General -> Build and wait until the build is done.
  6. Finally, click on esp32-2432S028Rv3 -> General -> Upload ad Monitor to flash the code into your board.

Secrets

Secrets are stored in a file called keys.txt, located in the root of an SD card. It must follow the format shown below:

service_id,encoded_base_32_secret

Each service must be added on a new line. For example:

aws-1,DSAJDHHAHASAUDOASNOTREALOADAKLDASAJFPOAIDONTEVENTRYOASFAIPO
aws-2,DSAJDHHAHASAUDOASNOTREALOADAKLDASAJFPOAIDONTEVENTRYOASFAIPO
aws-3,DSAJDHHAHASAUDOASNOTREALOADAKLDASAJFPOAIDONTEVENTRYOASFAIPO

WARNING: for now, secrets must be unencrypted and based 32 encoded. In the future, Users will have the option to encrypt their secrets, and ask for fingerpint/pin/password before retrieving the current TOTP. The plan is to make this feature configurable per service.

WARNING: file must end with a new line.

How to verify if it is working

  1. Go to https://totp.danhersam.com/
  2. Paste/type your encoded base 32 secret in the secret field, and then compare the TOTP code shown with the one you are seeing on the ESP32's screen.

Registering Secrets via local network with MQTT

To enable saving secrets to ESP32 via a local network, this project uses MQTT as the messaging protocol, Node-red as the postman (per say) and Eclipse Mosquito as the MQTT broker. Both services are started using a docker compose, in order to ease the setup. So, before continuing, install Docker on your computer following the guide found here.

After that, run the following script to start both node-red and the mqtt broker:

./scripts/start-node-red.sh

WARNING Make sure to have the following ports free before running ./scripts/start-node-red.sh: 1880 (node-red), 1883 (eclipse/mosquitto), 9001 (eclipse/mosquito).

You should see the following containers in the docker app.

and both container must not contain any error messages.

WARNING remember to assign static ips to the host running the MQTT service, as weel as for the esp32, in your router. This is required to avoid having to update the MQTT_SERVER constant with a new ip every time your router decides to change the ip of your host.

WARNING if your host can't receive messages from other devices on the same network, it could be a firewall problem. Configure the firewall in the host to enable it to receive requests from other devices on your local network.

After services have initialized, open node-red at localhost:1880, and import ./node-red/insert-secret.json flow.

Roadmap

  • enable the UI to display multiple TOTP codes at once. ✅

    why?

    R: people often use multiple services that require MFA TOTP codes with high frequency because of their short living sessions.

  • read encrypted keys from the sd card, and ask for password/pin/fingerprint during resets in order to decipher them

    why?

    R: it is not secure to have unencrypted secrets stored without protection

  • enable ESP32 to receive secrets via a local network, using a secure channel.

    why?

    R: ease the process of adding new services. With this feature I won't need to insert the SD card on my computer. If there is no SD card on the board, the channel to register new services is going to be closed. I also plan to require fingerprint/pin/password before opening this channel.