Qué puede hacer un atacante que conoce el código de la firmware pero no tiene acceso a los secretos del dispositivo.
La firmware es pública, así que los formatos de paquete, las longitudes y los algoritmos (X25519, HMAC-SHA256, HKDF) son conocidos. BLE es aire abierto, así que los anuncios y las escrituras GATT pueden escucharse. Ni en la firmware ni en el aire hay un solo secreto.
Dónde viven las claves:
owner_secret (32 bytes) — solo en la NVS del controlador y en el teléfono del propietario. Se deriva mediante ECDH X25519 durante el emparejamiento y nunca viaja en claro por el aire.guest_key — en el teléfono del invitado. Llega del servidor dentro de un paquete por un canal WSS seguro, nunca por BLE.| Attack | Defence |
|---|---|
| Falsificar un FIRE | HMAC-SHA256. Sin owner_secret ni guest_key eso es un 2128 de fuerza bruta: inviable. |
| Falsificar una escritura TIME | El mismo HMAC sobre bleId + epochMs. |
| Repetir un anuncio interceptado | El contador es monótono: ante una repetición o una disminución el HMAC no cuadra. |
| Repetir un fire interceptado | El anillo de nonces invalida los ya usados. |
| Atrasar el reloj del controlador | El trinquete: cualquier TIME < trinquete se rechaza. El trinquete vive en la NVS y sobrevive a un corte de corriente. |
| Usar un paquete caducado | Tras el funcionamiento del controlador, el trinquete es al menos la última hora real. En un paquete caducado validUntil queda por debajo del trinquete, así que el fire falla por caducidad. |
| Adelantar el reloj un año y dejar fuera a todos los invitados | Un tope de +24 h por cada escritura TIME de invitado. El propietario no tiene tope: se confía en él. |
| Emparejarse como segundo propietario sobre el primero | Tras el emparejamiento el controlador guarda owner_secret en la NVS y no vuelve al modo de emparejamiento sin un reinicio de fábrica. |
Requires: un paquete de invitado válido y no caducado.
Action: cada vez que esté en alcance, escribir un TIME que salte 24 horas hacia delante.
Limit: +24 h por sesión, y hace falta un fire reciente si la última sincronización fue hace menos de una hora. Tras diez visitas el trinquete va diez días por delante y los tokens del resto de invitados legítimos parecen caducados.
La otra cara: el token del propio atacante se juzga con esa misma hora falsa y por eso caduca antes.
Qué hacer: el propietario se acerca y escribe la hora real. El trinquete no se mueve, porque no puede retroceder, pero los nuevos tokens de invitado se emiten con un validUntil actual y a partir de ahí funcionan para todos. Recuperarse no exige más que refrescar la validez en el servidor.
Severity: una denegación de servicio temporal hasta que pase el propietario. No es crítico.
Requires: estar físicamente al alcance del controlador durante la ventana de 90 segundos posterior a iniciar el emparejamiento.
Action: sustituir en el saludo de emparejamiento su propia clave pública X25519.
Result: el controlador se empareja con el atacante y no con el propietario. El propietario cree que el emparejamiento salió bien pero no puede abrir nada desde el teléfono.
Defensa actual: solo la ventana de tiempo y la presencia física del propietario: ve que el emparejamiento no se confirmó y lo repite.
Hardening: añadir un código de verificación sencillo —cuatro dígitos en la pantalla del controlador o en Serial— que el propietario confirme en la aplicación. Hoy ese código no existe.
Severity: la ventana es estrecha y exige estar cerca justo en esos 90 segundos. Difícil de lograr en la práctica.
Requires: acceso físico al controlador y una pulsación larga del botón.
Action: La NVS se borra, owner_secret se elimina y el controlador vuelve al modo de emparejamiento.
Result: el atacante empareja el controlador consigo mismo y el propietario pierde el acceso.
Defence: montar el controlador en un lugar protegido: dentro de una caja, bajo una tapa.
Severity: lo mismo que arrancar un portero automático de la puerta: no es un problema criptográfico.
Requires: acceso físico y un volcado de la flash con el arranque seguro y el cifrado de flash desactivados: hoy lo están.
Action: leer la NVS donde está owner_secret.
Result: compromiso total del dispositivo y de todos los tokens de invitado.
Defence: activar el arranque seguro y el cifrado de flash del ESP32 (efuse, irreversible). Hoy no está activado.
Severity: aceptable para uso doméstico. En instalaciones críticas hace falta arranque seguro.
Requires: estar al alcance del BLE.
Action: conectarse y no desconectarse. NimBLE admite solo un número limitado de conexiones simultáneas.
Result: los clientes legítimos no pueden conectarse mientras el atacante ocupa una ranura.
Defensa actual: solo los tiempos de espera de NimBLE.
Hardening: añadir un tiempo de inactividad: si el cliente no realiza un FIRE válido en 5 segundos, cortar la conexión.
Severity: una denegación de servicio temporal mientras el atacante esté cerca.
Action: bombardear el controlador con escrituras basura.
Result: el controlador las descarta y las anota en Serial. Su estado no cambia y, en cuanto el atacante se va, todo funciona como siempre.
Tres puntos parecen protección y no lo son. Mejor nombrarlos que dejar que alguien se apoye en ellos.
El byte de estado del anuncio lleva una etiqueta de 16 bits. Basta contra una corrupción accidental en el aire y nada más: un falsificador decidido prueba 16 bits en segundos. El byte solo gobierna el indicador de abierto/cerrado en la tarjeta y no concede ningún permiso, por eso la etiqueta sigue siendo corta: cada byte de más cuesta batería en cada despertar.
Las claves solo para la aplicación se apoyan en un secreto compilado dentro del APK. Quien desempaquete el archivo obtiene el secreto y falsifica la atestación. Eso sube el listón frente a un cliente web ocasional, nada más. El control real de una clave así es atarla a la huella de un dispositivo: esa comprobación corre en el servidor y no depende de que la aplicación guarde un secreto.
La caducidad de una clave de invitado la impone el propio controlador en BLE: el token lleva un plazo y el controlador tiene reloj. En los demás tipos de objeto no hay plazo alguno: la clave vive hasta que el propietario la revoca. La aplicación no afirma lo contrario, pero si algún día aparece un límite de tiempo en la interfaz también para ellos, habrá que imponerlo en el servidor y no dibujarlo en la tarjeta.
Endurecimiento estándar para uso industrial:
Nada de esto es crítico para los escenarios actuales, pero las instalaciones críticas merecen ese refuerzo.