¿Qué pasa si alguien consigue tu contraseña del correo? Con una llave de seguridad FIDO2, nada: aunque la tenga escrita en un papel, no puede entrar sin un pequeño dispositivo USB que está en tu llavero y que solo responde cuando lo tocas con el dedo. Las llaves comerciales hacen justamente eso, pero no hace falta comprar una para entender cómo funcionan ni para tener la tuya.

En esta guía vas a armar una llave FIDO2 con el microcontrolador nRF52840 de Nordic y OpenSK, el firmware abierto de Google para llaves de seguridad. El diseño de referencia es de Circuit Digest: una placa de 26,5 × 11 mm con conector USB-C, un sensor táctil capacitivo en vez de botón y un LED RGB de estado.

Al terminar vas a saber:

  • qué ocurre por dentro cuando un sitio te pide "toca tu llave de seguridad", y por qué eso frena el phishing;
  • qué hace cada bloque del circuito y por qué se eligió cada componente;
  • cómo grabar el bootloader por SWD con OpenOCD, cargar OpenSK por USB y compilarlo desde el código fuente;
  • cómo probar la llave y qué revisar si no aparece.

El concepto: qué hace una llave FIDO2

FIDO2 es el nombre del conjunto de estándares abiertos que reúne a WebAuthn (la parte del navegador) y CTAP2 (el protocolo entre el computador y la llave). Chrome, Firefox, Safari y Edge lo soportan, igual que Google, GitHub, Microsoft y muchos otros servicios. Funciona en dos momentos.

Registro. La primera vez que agregas la llave a una cuenta, la llave genera un par de claves criptográficas nuevo solo para esa cuenta. Le entrega al sitio la clave pública y se guarda la privada. La clave privada nunca sale de la llave: no la ve el navegador, ni el sistema operativo, ni el sitio.

Inicio de sesión. El sitio manda un desafío (un número aleatorio) que el navegador le reenvía a la llave. La llave espera a que la toques, firma el desafío con la clave privada de esa cuenta y devuelve la firma. El sitio la verifica con la clave pública que guardó al registrarte. Si cuadra, sabe dos cosas: que es la credencial correcta y que había una persona con la llave en la mano, no un script.

Diagrama del flujo de autenticación FIDO2 con el nRF52840

Por qué resiste el phishing (lo que no cuenta el original)

Que la clave privada no salga del dispositivo es la mitad de la historia. La otra mitad es que cada credencial queda amarrada al dominio del sitio. Cuando te registras en accounts.google.com, la llave asocia la credencial a ese identificador. Si mañana un correo falso te lleva a accounts-google.security-check.com, el navegador le informa a la llave el dominio real que la está llamando, la llave no encuentra ninguna credencial para él y no firma nada. Con un código de SMS o de una app de autenticación pasa lo contrario: lo copias en la página falsa sin darte cuenta y el atacante lo reenvía al sitio real.

Además, cada firma incluye un contador que sube en cada uso. Si alguien lograra clonar una credencial, el sitio vería contadores que retroceden o se repiten y podría bloquearla.

El transporte: USB HID

En este diseño la llave habla con el computador por USB HID, la misma clase de dispositivo que usan teclados y mouse. La ventaja es práctica: los sistemas operativos ya traen el driver, así que no hay nada que instalar en el computador para usarla.

El hardware: por qué un nRF52840

Una llave FIDO2 necesita criptografía rápida, números aleatorios de buena calidad, memoria para el firmware y USB. El nRF52840 trae todo eso en un solo chip:

  • Arm Cortex-M4 con FPU a 64 MHz. Poco comparado con un procesador de aplicaciones, pero de sobra para correr el protocolo FIDO2, el USB y las operaciones criptográficas.
  • 1 MB de flash y 256 KB de RAM. Caben OpenSK, el sistema operativo Tock sobre el que corre y el almacenamiento de credenciales, sin memoria externa.
  • CryptoCell-310. Un bloque de hardware dedicado que acelera AES, SHA, HMAC, RSA y curvas elípticas (ECC), y que incluye un generador de números aleatorios verdadero (TRNG).
  • USB 2.0 Full Speed nativo. Sin conversor USB serial externo: menos componentes y una placa más chica.
  • Protección de acceso al firmware, que se puede activar para impedir que alguien lea la memoria por el puerto de depuración.

El TRNG merece una explicación aparte. Una firma con curva elíptica (ECDSA) usa en cada operación un número aleatorio de un solo uso. Si ese número se repite o es predecible, con dos firmas se puede despejar la clave privada. Le pasó a la PlayStation 3 en 2010 y a varias billeteras de Bitcoin en Android en 2013. Un generador por hardware basado en ruido físico elimina esa clase de fallas, y por eso importa tanto en una llave de seguridad.

El chip también tiene radio de 2,4 GHz (Bluetooth Low Energy, Thread, Zigbee) e interfaz NFC-A, pero esta versión de la placa no trae antena ni circuito de adaptación: es solo USB. Una revisión futura podría sumar NFC para autenticarse acercando el celular, como hacen las llaves comerciales.

El circuito, bloque por bloque

Esquemático de la llave FIDO2 con nRF52840

Conviene leer el esquemático siguiendo el camino de la energía y de los datos:

  1. Entrada USB-C (J1). Alimenta la placa y lleva los datos USB. Para que un cargador o un computador con USB-C le entregue 5 V, el conector necesita sus resistencias de 5,1 kΩ en los pines CC (en la lista de materiales aparecen dos).
  2. Protección (SP0503BAHTG). Justo después de J1 hay un arreglo de diodos TVS que descarga a tierra los picos de tensión y la electricidad estática. Una llave se enchufa y se saca miles de veces, muchas veces con la mano cargada; sin este diodo, la primera descarga fuerte puede matar los pines USB del microcontrolador.
  3. Reloj (Y1). Un resonador Murata de 32 MHz junto al chip, con sus condensadores de carga C12 y C13 para que oscile bien. El USB exige un reloj preciso, y el oscilador interno del nRF52840 no alcanza esa precisión.
  4. Desacople. Los condensadores pequeños repartidos por la placa filtran la alimentación de cada pin de energía del chip. Van lo más cerca posible del pin al que atienden.
  5. Presencia del usuario (U2, AT42QT1010). En vez de un botón para aprobar el inicio de sesión, la placa usa este sensor táctil capacitivo conectado a un pad de cobre (J2). Tocas la llave y U2 entrega una señal digital al nRF52840.
  6. Pulsador DFU (SW1). Un botón aparte que solo sirve para entrar al modo de actualización de firmware.
  7. LED RGB. Muestra el estado: esperando que toques, procesando, modo DFU.

Por qué un sensor táctil en vez de un botón

El AT42QT1010 es un chip de un solo canal que se calibra solo: mide la capacidad del pad al encender y después detecta el aumento que produce el dedo. Al microcontrolador le entrega un simple pin alto o bajo, así que el firmware lo trata como si fuera un botón. A cambio, la llave no tiene piezas móviles que se gasten, puede ir sellada y es más cómoda de usar mientras cuelga del computador.

Un detalle práctico: como se calibra al arrancar, no toques el pad mientras enchufas la llave. Si lo haces, toma tu dedo como el estado de reposo y queda insensible hasta que lo desconectes.

La placa

Diseño de la PCB de la llave FIDO2 con sus dos capas

La PCB es de dos capas para mantener bajo el costo de fabricación y mide 26,5 mm de largo por 11 mm de ancho, el formato de un dongle USB. Todos los componentes van por arriba, salvo el pulsador DFU, que queda por debajo para no apretarlo sin querer.

Render 3D de la PCB de la llave FIDO2

Los archivos Gerber están en el repositorio del proyecto (enlace en Recursos), listos para mandar a fabricar.

PCB de la llave FIDO2 recién fabricadas, sin componentes

El autor soldó la placa a mano con un esténcil de pasta y una placa calefactora. Ojo con la dificultad: casi todo es encapsulado 0402 y el nRF52840 viene en un aQFN de 73 pines con la almohadilla por debajo, imposible de soldar con cautín. Si es tu primera placa SMD, considera pedirla ensamblada al fabricante o partir por la variante con placa comercial que aparece más abajo.

PCB de la llave FIDO2 ensamblada a mano

El firmware: OpenSK más un bootloader UF2

OpenSK es una implementación abierta de llave de seguridad FIDO2, escrita en Rust por Google, que corre sobre Tock, un sistema operativo para microcontroladores. El proyecto no la usa tal cual: le aplica un parche para adaptarla a esta placa (el pin del sensor táctil, el LED RGB, el pulsador) y le agrega un bootloader que acepta archivos UF2 por USB. Con eso, después de la primera grabación ya no necesitas programador: actualizar es arrastrar un archivo.

El trabajo tiene dos etapas: una sola vez por SWD para el bootloader y, de ahí en adelante, por USB para el firmware.

Etapa 1: grabar el bootloader por SWD

Necesitas un programador compatible con DAPLink/CMSIS DAP. Una Raspberry Pi Pico con el firmware Picoprobe (o su sucesor, debugprobe) sirve perfecto. Un SEGGER J-Link también funciona. El procedimiento usa OpenOCD y corre en Windows, macOS o Linux; en Windows puedes usarlo directo o desde WSL2 pasándole la sonda al Linux.

Conecta el programador a la placa con cuatro líneas: SWDIO, SWCLK, GND y VTref (la referencia de voltaje del objetivo). La placa tiene que estar alimentada antes de programar.

Instala OpenOCD. En macOS, con Homebrew:

Bash
brew install openocd

En Debian, Ubuntu y derivados:

Bash
sudo apt update
sudo apt install openocd

En Windows, instala una versión compilada para Windows y deja el ejecutable en el PATH. En cualquier sistema, comprueba la instalación con:

Bash
openocd --version

Ahora graba el bootloader. Reemplaza xxx_bootloader-xxxx.hex por el nombre real del archivo HEX que bajaste del repositorio. Con DAPLink/CMSIS DAP:

Bash
openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg -c "init" -c "nrf52_recover" -c "nrf5 mass_erase" -c "flash write_image xxx_bootloader-xxxx.hex" -c "flash verify_image xxx_bootloader-xxxx.hex" -c "reset run" -c "shutdown"

Con J-Link:

Bash
openocd -f interface/jlink.cfg -f target/nrf52.cfg -c "init" -c "nrf52_recover" -c "nrf5 mass_erase" -c "flash write_image xxx_bootloader-xxxx.hex" -c "flash verify_image xxx_bootloader-xxxx.hex" -c "reset run" -c "shutdown"

Qué hace cada parte del comando, para que no lo corras a ciegas:

  • nrf52_recover borra el chip completo y desactiva la protección de acceso (APPROTECT). Hace falta si el chip ya traía firmware protegido.
  • nrf5 mass_erase deja la flash en blanco.
  • flash write_image y flash verify_image graban el archivo y lo comparan byte a byte con lo escrito.
  • reset run arranca el programa y shutdown cierra OpenOCD.

Importante: las dos primeras órdenes borran todo. Si repites este paso sobre una llave que ya usabas, pierdes todas sus credenciales y tendrás que registrarla de nuevo en cada sitio.

Etapa 2: cargar OpenSK por USB

  1. Mantén apretado el pulsador DFU mientras conectas la llave al computador.
  2. El LED empieza a "respirar" y la llave aparece como una unidad de almacenamiento USB.
  3. Copia el archivo .uf2 de OpenSK que corresponde a tu placa a esa unidad.
  4. Al terminar la copia, la llave se reinicia sola y arranca con el firmware nuevo.

Cada actualización futura es repetir estos cuatro pasos.

Compilar el firmware desde el código fuente

Los binarios del repositorio sirven tal cual. Compila solo si vas a cambiar algo: el pin del sensor, los colores del LED, las opciones de OpenSK.

Clona OpenSK (versión 2.1) y corre su script de preparación, que instala Rust y las herramientas que necesita:

Bash
git clone -b 2.1 https://github.com/google/OpenSK.git
cd OpenSK
./setup.sh

Baja las herramientas de conversión a UF2 de Microsoft:

Bash
wget -P tools https://github.com/microsoft/uf2/raw/master/utils/uf2conv.py
wget -P tools https://github.com/microsoft/uf2/raw/master/utils/uf2families.json
chmod a+x tools/uf2conv.py

Aplica el parche del proyecto y copia los archivos de la placa Nordic:

Bash
git apply OpenSK_xxxx.patch
cp -r boards/nordic/. third_party/tock/boards/nordic

Compila OpenSK para la placa con bootloader DFU:

Bash
./deploy.py --board=nrf52840_dongle_dfu --programmer=none --opensk

Y convierte el HEX resultante a UF2:

Bash
./tools/uf2conv.py -c -f 0xada52840 \
   -o target/nrf52840_dongle_dfu_merged.uf2 \
   target/nrf52840_dongle_dfu_merged.hex

El 0xada52840 es el identificador de familia del nRF52840: el bootloader lo revisa y rechaza cualquier UF2 hecho para otro chip, así que un error de copia no deja la llave inservible. Los archivos generados quedan en la carpeta target y se cargan con el modo DFU de la etapa 2.

Pruebas: comprobar que la llave funciona

Antes de amarrarla a cuentas importantes, pruébala en un sitio de demostración de WebAuthn como webauthn.io:

  1. Abre el sitio, escribe un nombre de usuario cualquiera y elige registrar.
  2. El navegador te pide la llave: el LED cambia de estado y espera tu dedo. Toca el pad.
  3. Ahora inicia sesión con el mismo usuario. Debería volver a pedir el toque y entrar.

Si funciona, agrégala a tu cuenta de Google o de GitHub desde la configuración de seguridad (la opción aparece como "llave de seguridad" o "passkey").

Si algo falla:

  • La llave no aparece en el computador. Revisa la soldadura del conector USB-C y del resonador de 32 MHz: sin reloj estable el USB no enumera. Un cable USB-C que solo carga, sin líneas de datos, produce el mismo síntoma.
  • Funciona en Windows pero no en Linux. El navegador necesita permiso para acceder al dispositivo HID; la documentación de OpenSK trae la regla udev que lo habilita.
  • El toque no responde. Desconecta la llave y vuelve a conectarla sin tocar el pad, para que el AT42QT1010 se recalibre. Si sigue igual, revisa la soldadura del sensor y la del pad.
  • OpenOCD no encuentra el chip. Revisa que VTref esté conectado y que la placa esté alimentada; con VTref suelto, muchas sondas no levantan las líneas SWD.

Una advertencia honesta: una llave hecha en casa es un excelente proyecto para aprender y experimentar, pero no tiene la certificación ni el elemento seguro de una llave comercial, y el propio OpenSK se presenta como una plataforma de investigación. Si la usas en cuentas reales, registra siempre un segundo método de recuperación (otra llave o los códigos de respaldo del servicio): si la llave se pierde o se borra, sin respaldo te quedas fuera de la cuenta.

Variantes y mejoras

  • Sin fabricar placa: una nRF52840 comercial con un TTP223. OpenSK soporta oficialmente el Nordic nRF52840 Dongle, que ya trae USB y botón. Si quieres un pad táctil como en este diseño, puedes agregar un módulo táctil capacitivo TTP223 a un GPIO libre y apuntar el botón de presencia de OpenSK a ese pin en el archivo de la placa. También existen placas pequeñas como la XIAO nRF52840 o la Pro Micro nRF52840, pero en ese caso tienes que escribir tú el archivo de placa para Tock (pines del LED, del botón y del reloj), y es un proyecto en sí mismo.
  • Activar la protección de lectura. Una vez que la llave funciona, puedes activar APPROTECT para que nadie pueda leer la flash por SWD. A partir de ahí, la única forma de reprogramarla por ese puerto es nrf52_recover, que borra todo, credenciales incluidas. Es exactamente lo que quieres en una llave que puede perderse.
  • NFC en una segunda versión. El nRF52840 ya trae la interfaz NFC-A. Con una antena de PCB en espiral y los condensadores de sintonía en los pines NFC1 y NFC2, más el soporte NFC de OpenSK, la llave podría autenticarte en el celular solo con acercarla.
  • Carcasa impresa en 3D. Una funda de PLA o PETG con una ventana delgada sobre el pad protege la placa en el llavero. El AT42QT1010 detecta el dedo a través de 1 a 2 mm de plástico; si cambias el grosor, ajusta el condensador de sensibilidad del sensor.

Personalización para Chile

La placa de este proyecto usa componentes SMD muy específicos (el nRF52840-QIAA suelto, el AT42QT1010, el resonador Murata de 32 MHz, el TVS SP0503BAHTG) que se compran a distribuidores como DigiKey o Mouser, y la PCB se manda a fabricar con los Gerber del repositorio.

En MechatronicStore puedes conseguir lo que rodea al montaje y lo necesario para la variante sin placa propia:

  • Raspberry Pi Pico: como programador SWD con el firmware Picoprobe o debugprobe, para grabar el bootloader una sola vez.
  • Placa con nRF52840 (por ejemplo, de la familia XIAO): para la variante con placa comercial.
  • Módulo táctil capacitivo TTP223: reemplaza al AT42QT1010 y al pad de la PCB en esa variante; entrega la misma señal digital alta o baja.
  • LED RGB: el indicador de estado, si tu placa no trae uno.
  • Cable USB-C con datos: para conectar la llave y cargar el firmware (que no sea un cable solo de carga).
  • Jumpers hembra hembra: para unir la Pico a los pines SWD de la llave.

Si en el tutorial original usan un SEGGER J-Link, una Raspberry Pi Pico con debugprobe cumple la misma función para esta tarea.

Recursos

Versión chilena con componentes en stock local en MechatronicStore.