AllanOricil / esp32-mfa-authenticator

ESP32 MFA Authenticator
https://allanoricil.github.io/esp32-mfa-authenticator/
MIT License
58 stars 0 forks source link
2fa authenticator esp32 mfa totp

Build with PlatformIO Contributor Covenant CI

ESP32 MFA Authenticator

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:

Other motives:

🌐 Install

You can flash your ESP32-CYD board with the latest build using this site.

[!IMPORTANT] Read the site and the github workflows source codes to verify that the build artifact is, in fact, the one from the latest release published in this repository.

[!NOTE] This site was based on https://esphome.github.io/esp-web-tools/

🎬 Demos

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

https://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/a398b55b-a415-4d21-8f28-91df153bac9f

https://github.com/AllanOricil/esp32-mfa-totp-generator/assets/55927613/b610d1de-1bf9-47fe-9148-8973cb30205d

βš™οΈ Parts

[!TIP] You can buy this board from Aliexpress clicking on any of these affiliate links: USD BRL

[!TIP] You can buy this acrillic case from Aliexpress clicking on any of these affiliate links: USD BRL

[!TIP] 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

[!NOTE] The above list doesn't consider expenses with taxes and shipping.

[!NOTE] Prices were taking in February 2024.

πŸ’» Dev Environment Requirements

dependency version
python >= v3.9
node >= v18.18
npm >= v10.2
vscode >= v1.87
platform.io ide vscode extension >= v3.3
docker >= v25.0

[!IMPORTANT] Don't forget to install a driver to allow your OS to recognize esp32

[!IMPORTANT] Node and npm, its package manager, are required because several development tools are 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.

[!TIP] 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.

πŸ”Œ Boot and Reset Requirements

βš™οΈ config.yml

# [REQUIRED] necessary for enabling future changes
version: 0.0.0

wifi:
  # [REQUIRED] (text) wifi connection password
  password: test
  # [REQUIRED] (text) wifi id
  ssid: test

mqtt:
  # [OPTIONAL] (text) mqtt server port
  port: 1883
  # [OPTIONAL] (text) mqtt server ip
  server: 192.168.0.1
  # [OPTIONAL] (text) mqtt connection username
  username: test
  # [OPTIONAL] (text) mqtt connection password
  password: test

security:
  # [OPTIONAL] (number) [default 3] board is locked and requires a hard reset, after N wrong unlock attempts
  max_number_of_wrong_unlock_attempts: 3
  pin:
    # [OPTIONAL] (text) pin code composed of numbers only and HMAC-SHA256 hashed
    hash: test
    # [OPTIONAL] (text) key used to hash pin code
    key: test

display:
  # [OPTIONAL] (number) [default 10] if provided, the display will turn off after n seconds have passed
  sleep_timeout: 10

touch:
  # [OPTIONAL] (bool=false|0) calibrate touch sensor if true or 1
  force_calibration: 0

[!IMPORTANT] Upon the initial boot, it is imperative to undergo the calibration process at least once, as outlined in the How to build section below.

[!TIP] Once the boot process is finished, remove the SD card from the board, and store it somewhere safe. Before rebooting, or if you want to add new secrets, remember to put it back in the board.

πŸ“– Guides

πŸ“š How to build

πŸ“š Using Platform IO IDE in VS Code

  1. Install Platform IO IDE extension in VS Code.
  2. Open this project in a new vscode workspace, and wait for Platform.IO to finish its automatic setup.
  3. Open platformio.ini and edit
  4. Connect your board to your computer. If you installed the right drivers, the next steps should work just fine.
  5. Click on the Platform.IO button, in VS Code's sidebar.
  6. Then click on esp32-cyd -> General -> Build and wait until the build is finished with success.
  7. Orient the board horizontally, placing the USB port(s) on the right side and positioning the screen towards you. This step is important for when calibrating the touch sensor.
  8. Finally, click on esp32-cyd -> General -> Upload ad Monitor to flash the code into your board, and verify its outputs using a terminal.

πŸ“š Using Platform IO CLI

Alternatively, if you prefer using CLIs, install PlatformIO's official CLI using this tutorial, and then follow the next steps:

  1. Run platformio device list and annotate the device path of your board.

[!TIP] You can discover which path belongs to your board by comparing the outputs of this command when your board is connected and not.

  1. Run platformio run --environment esp32-cyd to build the application
  2. Orient the board horizontally, placing the USB port(s) on the right side and positioning the screen towards you. This step is important for when calibrating the touch sensor.
  3. Upload the code to your board using platformio run --target upload --upload-port ${DEVICE_PATH} --target monitor --environment esp32-cyd.
  4. Wait until the screen turns white, then proceed to follow the calibration instructions displayed in your terminal for the touch sensor.

[!IMPORTANT] The pin screen won't work if you did not calibrate the touch sensor using the orientation described above in step 3.

[!IMPORTANT] Remember to substitue ${DEVICE_PATH} with the value you got in step 1.

[!NOTE] The calibration process occurs only once, immediately after flashing the board and during its initial boot.

πŸ“š How to add TOTP Secrets

The secrets used to compute TOTP codes must be stored in a file called keys.txt, and be placed 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

[!IMPORTANT] This file must end with a new line.

[!IMPORTANT] Secrets must be unencrypted and based 32 encoded. In the future, my plan is to decrypt secrets using a secret stored in the board. With this approach, a stolen SD card won't work on a different board flashed with the same code unless the board has the same key.

πŸ“š How to verify if TOTP codes are correct

  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.

πŸ“š How to recalibrate the touch sensor

  1. Add the following property to the root of your config.yml and save it.
touch:
  force_calibration: true

[!NOTE] The calibration process will initiate upon the initial boot of the board, regardless of the content stored in config.yml.

  1. Connect the board to your computer.
  2. Open a terminal, and run platformio device monitor --baud 115200 to monitor the board's serial port. monitor-serial-port
  3. Orient the board horizontally, placing the USB port(s) on the right side and positioning the screen towards you.
  4. Ensure the SD card containing config.yml is on the board.
  5. Press the RST button on the board and wait until the screen becomes pure white, and follow the instructions as shown below.
  6. Remove the touch properties from config.yml, save it and put it back on the board.
  7. Press the RST button once more, and now verify that the calibration flow isn't triggered anymore.

[!IMPORTANT] The pin screen won't work if you did not calibrate the touch sensor using the orientation described above in step 4.

πŸ“š How to register TOTP Secrets without inserting the SD card into a computer

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 can be started using docker compose using a docker-compose.yaml file located in the root of this project, 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

Open node-red at http://localhost:1880, and then load the flow from ./node-red/insert-secret.json. For now you must manually setup the node input with the secret you want to send to your ESP32, but in the future I plan to have a small app and a chrome extension as clients using the local node-red services to ease the process of storing secrets on the board.

[!IMPORTANT] 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.

[!IMPORTANT] Remember to assign static IPs to the host running the MQTT service, as weel as for the esp32, in your router. This is a suggestion 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.

[!TIP] 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 all services have initialized, open node-red at localhost:1880, and import ./node-red/insert-secret.json flow. Use this tutorial to guide you to import flows into node-red.

[!IMPORTANT] Remember to put the SD card again in the board, if you want the secret to be stored. If you don't do it, after a reset the TOTP code for that secret won't appear because the secret wasn't written to disk.

🎯 Roadmap

βœ… Display multiple TOTP codes

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

βœ… Unlock with PIN Code

It is not secure to have unencrypted secrets stored without protection

βœ… Add secrets via a local network, using a secure channel

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.

βœ… Manage board settings using a browser

Users can manage their board settings using a browser. Once the board is connected to the local network, open a browser and type {IP_ADDRESS}/esp32/settings.

[!IMPORTANT] At the moment, secrets must be re-entered before submiting the form. If you don't re-type the secrets, they will be stored as ******* in the SD card, and this will break the boot.

βœ… Display turns off automatically after N seconds without user interaction

After booting, the display turns off automatically if it doesn't receive touch events after N seconds. N is a configurable in the config.yml.

πŸ”œ Create chrome extension to ease registering TOTP secrets

When the ESP32-MFA-Authenticator extension is enabled, a new button called "register secret" appears, in the browser's context menu, when right clicking over a QR code. When selecting this button, the registration flow starts.

πŸ”œ Group TOTP Codes

I work with a customer that has multiple AWS accounts, and each has its own MFA Virtual device. To help me to easily find the MFA TOTP codes for a group of accounts that belongs to the same customer, I decided to create a way to group TOTP codes together on its own separate view. Each group of TOTP secrets will result in a page on the TOTP Screen. The User can select the page by swiping to the right or left. With this feature, Users will be able to manage multiple virtual MFA devices for multiple customers using the same device. To further secure TOTPs for a group, the User will be able to set a PIN code for a group. If PIN code is set for that group, a PIN Screen appears before the group of TOTPs can be rendered. There will also still exist the Global PIN code, which protects all TOTPs.

βœ… Lock board after N wrong unlock attempts

Improve the validation function to block access to the board after few wrong attempts happened. With this enhancement, brute forcing all possible combinations won't be possible.

πŸ”œ Unlock with fingerprint

Instead of typing a pin code, it will be possible to unlock the board using a fingerprint. The goal is to ease the access to the TOTP codes, while maintaining them secure. It will also work globally or by group.

πŸ’– Become a Sponsor

If this device has made your life easier, consider supporting its development by clicking the button below.

<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="width: 217px;" />