01 · Primero, contra quién
El modelo de amenazas
La seguridad no se agrega al final como una capa de protección universal: se diseña contra amenazas concretas, y empieza con tres preguntas. ¿Qué hay que proteger? (los activos). ¿De quién? (los atacantes, con sus medios y su motivación). ¿Por dónde pueden entrar? (los vectores). Sin esas respuestas, uno termina gastando esfuerzo en proteger lo que a nadie le interesa y dejando abierta la puerta que sí usarán.
| Activo | Qué se pierde si se compromete |
|---|---|
| El firmware (la integridad del programa que corre) | El atacante controla el equipo: lo usa de plataforma de ataque, lo hace comportarse mal, roba lo que pasa por él. Es el activo más crítico |
| La propiedad intelectual (el código y los algoritmos) | Se copia el producto o se lo clona; se pierde la ventaja competitiva |
| Las claves y credenciales | Se suplanta al equipo, se descifra el tráfico o, peor, se compromete a toda la flota si la clave es común |
| Los datos del usuario | Privacidad, responsabilidad legal y confianza en la marca |
| La función del equipo (disponibilidad) | Un atacante lo deja fuera de servicio, o lo usa para dañar algo físico (una cerradura, un calefactor, una bomba) |
| Atacante | Medios | Ataques típicos | Defensa principal |
|---|---|---|---|
| Remoto (por la red) | Un script, miles de equipos a la vez | Contraseñas por defecto, vulnerabilidades conocidas, un servidor de actualizaciones falso | Sin credenciales por defecto, actualización firmada, protocolos autenticados, superficie mínima |
| Cercano (radio, bus, usuario) | Un dispositivo en la proximidad; acceso a un puerto | Interceptar o inyectar mensajes, un cargador por cable sin autenticación | Autenticación en cada interfaz; cifrado de enlace; deshabilitar los puertos que no se usan |
| Físico con herramientas | El equipo en la mano, un programador, un osciloscopio | Leer la flash por el puerto de depuración, clonar el producto, cambiar el firmware, inyectar fallas de tensión | Protección de lectura, cierre del puerto de depuración, arranque seguro, cifrado de la flash |
| De la cadena de suministro (interno o proveedor) | Acceso a la compilación, a la fábrica o a las claves de firma | Un firmware modificado antes de firmarse; la clave de firma robada | Claves de firma en un módulo de seguridad (HSM) fuera de línea, compilaciones reproducibles, separación de funciones |
Un ejemplo que ilustra por qué importa pensar en el atacante más barato: en 2016, la botnet Mirai infectó cientos de miles de cámaras y routers domésticos simplemente probando unas pocas decenas de contraseñas de fábrica por Telnet. No usó ninguna técnica sofisticada: aprovechó equipos que salían de fábrica con credenciales conocidas, sin forma de cambiarlas ni de actualizarse. La lección es doble: las vulnerabilidades más explotadas son las más tontas, y un producto sin mecanismo de actualización segura está condenado, porque sus defectos de hoy serán sus incidentes de mañana.
02 · Detectar daños y detectar mentiras
Integridad, autenticidad y firma digital
En el tema 42 usamos un CRC para comprobar que una imagen llegó sin daños. Ese es un problema de integridad accidental: el ruido o un error alteran algunos bits, y el CRC lo detecta. Pero frente a un atacante, el problema es otro: es autenticidad, saber que la imagen la hizo quien dice haberla hecho, y que nadie la modificó con intención. Son propiedades distintas, y se resuelven con herramientas distintas, que conviene no confundir:
| Mecanismo | Detecta daños accidentales | Detecta una modificación intencional | Demuestra quién lo hizo | Clave |
|---|---|---|---|---|
| CRC | Sí | No: se calcula sin ningún secreto y se puede falsificar | No | Ninguna |
| Hash criptográfico (SHA-256) | Sí | Sí, si el valor del hash viaja protegido | No (cualquiera puede calcular un hash) | Ninguna |
| HMAC (hash con una clave secreta) | Sí | Sí | Solo entre quienes comparten la clave | Una clave compartida: el equipo la necesita para verificar |
| Firma digital (ECDSA, Ed25519, RSA) | Sí | Sí | Sí: solo quien tiene la clave privada puede firmar | Un par: la privada firma (queda con el fabricante), la pública verifica (va en el equipo) |
La Parte 1 del programa de Python hace una demostración que conviene ver con los propios ojos: falsificar un CRC-32. Un atacante toma el firmware legítimo, cambia cuatro bytes del código por los suyos, y después calcula cuatro bytes de relleno que, agregados al final, hacen que el CRC de la imagen maliciosa sea exactamente igual al de la original: 0x09990583 en ambos casos. El cálculo es instantáneo, porque el CRC es una función lineal y matemáticamente invertible. Para el cargador del tema 42, la imagen maliciosa es perfecta. El hash criptográfico no se deja: el SHA-256 de la imagen original y el de la maliciosa no tienen nada en común, y no hay forma de elegir qué cambios producen un hash dado.
Y la propiedad de «avalancha» lo explica (Parte 2): cambiando un solo bit de una imagen de 4 KB, en promedio 128,1 de los 256 bits del SHA-256 cambian (el mínimo observado en 1000 pruebas fue 104; el máximo, 153): el resultado se comporta como un sorteo, sin patrones que el atacante pueda aprovechar. Con 256 bits, probar todas las posibilidades es imposible: 2256 ≈ 1,2 × 1077; aun a 1012 intentos por segundo, tomaría unos 1057 años.
Clave compartida frente a clave pública
Un hash por sí solo no autentica: el atacante puede calcular el hash de su imagen y reemplazar el valor. Hay que atar el hash a un secreto. Hay dos formas, con una consecuencia muy distinta para una flota de equipos:
- HMAC (clave compartida): el fabricante calcula una etiqueta con una clave secreta, y el equipo la verifica con la misma clave. Es simple y rápido, pero hay un problema grave: la clave tiene que estar dentro de cada equipo. Quien extrae la clave de un solo equipo (con un ataque físico) puede firmar cualquier imagen para toda la flota. La Parte 3 lo muestra: con la clave extraída, el HMAC de la imagen maliciosa verifica.
- Firma digital (clave pública): el fabricante firma con una clave privada que nunca sale de su sistema, y el equipo verifica con la pública. La clave pública permite verificar pero no firmar: robar un equipo no da nada con qué falsificar imágenes. Es lo que se usa en el firmware actual. La Parte 3 lo ilustra con una firma de juguete (RSA con números de 62 bits, que no es segura y está solo para ver el mecanismo): el equipo, con su clave pública, verifica la imagen original, y rechaza tanto la imagen maliciosa como una firma inventada por el atacante.
En la práctica se usan ECDSA sobre la curva P-256 o Ed25519 (claves de 256 bits, firmas de 64 bytes), o RSA de 2048 bits o más (firmas de 256 bytes). Las curvas elípticas son las preferidas en microcontroladores por su menor tamaño y velocidad. La firma no se calcula sobre toda la imagen sino sobre su hash, y se escribe en el encabezado (tema 42). Nunca se programa la criptografía por cuenta propia: se usa una biblioteca probada (mbed TLS, TinyCrypt, wolfSSL, libsodium) o el acelerador criptográfico del chip. Programar la propia criptografía es el error de seguridad más frecuente y más costoso.
03 · Desde el primer instante
El arranque seguro y la cadena de confianza
Verificar la firma cuando llega la imagen no alcanza: un atacante con acceso físico puede modificar la flash después, por el puerto de depuración o con un programador. El arranque seguro (secure boot) verifica la firma en cada arranque, antes de ejecutar, de modo que lo que corra sea siempre lo que el fabricante firmó. Pero ¿quién verifica al verificador? Si el cargador que comprueba las firmas se pudiera reemplazar, un atacante reemplazaría el cargador por uno que acepte todo. La solución es una cadena de confianza: cada eslabón verifica al siguiente, y el primero es inmutable.
Cada eslabón cumple un papel preciso. La ROM de arranque es código grabado por el fabricante del chip en una memoria de solo lectura: nadie puede modificarlo, y por eso es la raíz de confianza. Esa ROM verifica la firma del cargador contra una clave pública cuyo hash está grabado en fusibles de un solo uso (OTP o eFuses: una vez programados, no se borran). El cargador, a su vez, lleva embebida una clave pública (o su hash) con la que verifica la aplicación. Y la aplicación solo se ejecuta si esa verificación tuvo éxito. Un atacante que modifique cualquier eslabón rompe la firma y el equipo no arranca; uno que no tenga la clave privada no puede firmar un reemplazo.
/* En el cargador: verificar ANTES de ejecutar, en cada arranque */
typedef enum { V_OK, V_ENCABEZADO, V_HASH, V_FIRMA, V_RETROCESO } Verificacion;
Verificacion verificar_imagen(uint32_t ranura_base, uint32_t tam_ranura) {
const EncabezadoImagen *h = (const EncabezadoImagen *)ranura_base; /* el encabezado de 42, ahora con firma */
if (!encabezado_coherente(h, tam_ranura)) return V_ENCABEZADO; /* magia, producto, tamaño, enlace */
uint8_t hash[32];
sha256((const uint8_t *)(ranura_base + sizeof *h), h->tamano, hash); /* hash de TODO el código (o acelerador de hardware) */
if (memcmp_constante(hash, h->hash, 32) != 0) return V_HASH; /* el contenido no es el que se firmó */
if (!ecdsa_p256_verificar(CLAVE_PUBLICA, hash, h->firma)) return V_FIRMA; /* firmado por quien tiene la clave privada */
if (h->version_seguridad < contador_seguridad_leer()) return V_RETROCESO; /* apartado siguiente */
return V_OK;
}
/* La clave pública vive en la zona protegida del cargador (o su hash en OTP). Se usa una biblioteca probada, no criptografía propia. */
Tres precisiones de diseño que marcan la diferencia entre una verificación que protege y una que solo lo parece:
- Verificar en cada arranque, no solo al instalar. Si la verificación ocurre solo cuando llega la imagen (en la descarga), un atacante con acceso físico puede modificar la flash después, y el equipo ejecutará lo que quiera (es el clásico error «de tiempo de verificación a tiempo de uso»). El costo es tiempo de arranque: la Parte 5 estima, con supuestos explícitos (25 ciclos por byte para el hash y 15 millones de ciclos para verificar una firma ECDSA en software), unos 270 ms para una imagen de 256 KB a 80 MHz, 449 ms a 48 MHz y 128 ms a 168 MHz. Un acelerador de hash por hardware reduce mucho la parte del hash; la firma pesa lo mismo. Es un costo aceptable para casi todos los productos, y se puede acortar verificando solo el encabezado firmado y el hash en cada arranque cuando la flash está protegida.
- Ejecutar solo lo verificado. La verificación y la ejecución deben referirse a lo mismo: si se verifica una copia en RAM y se ejecuta otra en flash que puede cambiar entre medio, la comprobación no vale. Y la clave pública debe estar donde el atacante no pueda cambiarla (zona protegida contra escritura o OTP): una clave pública modificable es una puerta abierta.
- Comparar en tiempo constante (
memcmp_constante, como la comparación del código en la cerradura del tema 27): una comparación que termina al primer byte distinto filtra, por su duración, cuántos bytes coinciden.
Los microcontroladores actuales ofrecen apoyo de hardware para esto, aunque los nombres y detalles varían por familia y hay que consultar la documentación de cada una: ROM de arranque con verificación de firma y claves en eFuses (por ejemplo, el arranque seguro del ESP32), niveles de protección de lectura y bloqueo del puerto de depuración (los niveles RDP de los STM32; APPROTECT en los nRF52), cifrado de la flash en el chip, zonas protegidas y, en los Cortex-M33, TrustZone, que separa un «mundo seguro» (claves, criptografía) de uno «no seguro» (la aplicación), de modo que un error en la aplicación no pueda leer las claves. Y no hace falta escribir el cargador desde cero: MCUboot es un cargador de código abierto que implementa firma (ECDSA, Ed25519, RSA), arranque de prueba con confirmación (el del tema 42) y protección contra el retroceso, y está portado a muchas familias.
04 · No volver al pasado
Protección contra el retroceso
La firma garantiza que una imagen es auténtica, no que sea reciente. Un atacante puede aprovecharlo: toma una versión vieja y legítima, firmada por el fabricante cuando era la última, con una vulnerabilidad que después se corrigió, y la instala en el equipo. La firma verifica perfectamente. Es el ataque de retroceso (rollback o downgrade), y obliga a que el equipo recuerde cuál es la versión mínima aceptable.
La solución usa un contador de versión de seguridad que solo puede crecer y que el atacante no puede bajar: se guarda en fusibles OTP o en una memoria con un contador monótono, no en la flash ordinaria. Cada imagen firmada lleva su versión de seguridad en el encabezado (cubierta por la firma, de modo que no se puede cambiar sin invalidarla). El cargador la compara con el contador y rechaza cualquier imagen con una versión menor. Cuando se confirma una imagen con una versión de seguridad mayor, el contador sube y no vuelve a bajar. La Parte 4 simula una secuencia de eventos:
| Evento | Decisión |
|---|---|
| Instalar la 1.1 (seguridad 1) | Aceptada |
| Instalar la 2.0 (seguridad 2, corrige una vulnerabilidad) | Aceptada; tras confirmar, el contador sube a 2 |
| El atacante instala la 1.1, firmada y legítima, para explotar la vulnerabilidad | Rechazada (seguridad 1 < contador 2) |
| Instalar la 2.1 (seguridad 2) | Aceptada |
| La 2.1 no pasa la prueba: volver atrás a la 2.0 (seguridad 2) | Aceptada: la vuelta atrás del tema 42 sigue funcionando |
| Volver atrás a la 1.1 | Rechazada (seguridad 1 < contador 2) |
Observá la distinción entre dos números de versión: la versión de funciones (1.1, 2.0, 2.1: la que ve el usuario) y la versión de seguridad (1, 2: la que cuenta el contador). Se separan a propósito. La vuelta atrás del tema 42 (de la 2.1 a la 2.0) debe poder funcionar, y como las dos tienen la misma versión de seguridad, el contador no la impide; en cambio volver a la 1.1, que tenía la vulnerabilidad, queda bloqueado. La versión de seguridad se sube solo cuando se corrige una vulnerabilidad (no con cada versión), precisamente para no cerrar la vuelta atrás sin necesidad.
El contador monótono tiene un costo: es irreversible. Los fusibles OTP tienen una cantidad finita de bits (algunos chips ofrecen decenas de incrementos), y subir el contador por error deja inutilizable la versión anterior. Se sube solo tras la confirmación de la nueva imagen, y con criterio.
05 · Lo que no debe salir del chip
Proteger el código, los datos y las claves
El arranque seguro protege la integridad (que corra lo firmado). Pero hay otro conjunto de amenazas que atacan la confidencialidad: leer lo que está dentro del equipo. Un atacante con un programador y un cable SWD puede, si el puerto de depuración está abierto, leer toda la flash: el código (y con él, el producto para clonarlo), las claves guardadas, los datos del usuario. Las defensas se apilan:
| Protección | Cómo funciona | Amenaza que cubre | Cuidado |
|---|---|---|---|
| Protección de lectura (en STM32, los niveles RDP; en nRF52, APPROTECT) | Bloquea la lectura de la flash y la RAM por el puerto de depuración; en algunos niveles, borra todo si se la quiere desactivar | Leer el código y las claves con un programador | El nivel máximo suele ser irreversible: el equipo no se puede volver a depurar ni reparar. Se aplica en producción, no en desarrollo |
| Cierre del puerto de depuración | Deshabilita SWD/JTAG de forma permanente o con una autenticación | Control total por depuración | Quita también al fabricante la forma de analizar fallas de campo: se compensa con registros (tema 37) |
| Cifrado de la flash | El chip cifra con AES lo que escribe y descifra al leer, con una clave que no sale del chip | Leer la flash externa o desoldar el chip; copiar la imagen | Si el chip está en funcionamiento y la depuración abierta, el contenido se lee descifrado |
| Actualizaciones cifradas | La imagen viaja cifrada (AES-GCM, ChaCha20-Poly1305) | Interceptar la imagen en el enlace y analizarla | Protege la confidencialidad, no sustituye a la firma; el cifrado con autenticación hace ambas cosas |
| Elemento seguro (secure element, por ejemplo ATECC608) | Un chip aparte que guarda claves y hace firmas y cifrado sin revelarlas nunca | Extraer una clave por ataque físico | Costo y un chip más; es la defensa más fuerte para la identidad del equipo |
| TrustZone-M y zonas protegidas | Separa el código y los datos sensibles (claves, criptografía) del resto, por hardware | Que un error de la aplicación exponga las claves | Requiere un diseño explícito de las dos partes |
Algunas reglas que atraviesan todas las protecciones:
- Un secreto común a toda la flota es una bomba. Una clave (o una contraseña) idéntica en todos los equipos equivale a que cualquiera que extraiga una comprometa todas (lo que mostró la Parte 3 con el HMAC). Cada equipo debe tener sus propias credenciales, generadas e instaladas en la fábrica (provisionamiento), o derivadas de un secreto maestro y del número de serie del equipo.
- Nunca credenciales por defecto ni claves escritas en el código. Una clave dentro del firmware está en cada copia del firmware, y quien lo lea la tiene. Es el error de Mirai.
- Números aleatorios de calidad. Las claves, los nonces y los canarios (tema 31) necesitan un generador de números aleatorios por hardware (TRNG). Un generador predecible (por ejemplo,
rand()con la semilla del reloj) vuelve inútil toda la criptografía que depende de él. - Borrado seguro: las claves en RAM se borran cuando ya no se usan, con una escritura que el compilador no elimine (
memset_so una escrituravolatile, como en el tema 27). - Ataques físicos más finos: con equipo especializado, un atacante puede medir el consumo o el tiempo de ejecución para deducir una clave (ataques de canal lateral) o provocar fallas con un pico de tensión para saltarse una comprobación (inyección de fallas, glitching). Las defensas son específicas (comparaciones en tiempo constante, comprobaciones duplicadas, hardware con contramedidas) y justifican un elemento seguro cuando el activo vale mucho.
Una advertencia sobre la ofuscación: ocultar el código con trucos (nombres confusos, saltos innecesarios) no es seguridad: retrasa a un atacante, no lo detiene, y da una falsa sensación de protección. La seguridad se apoya en mecanismos con garantías (firmas, claves, hardware de protección) cuyo funcionamiento puede ser público: el principio de Kerckhoffs dice que un sistema debe ser seguro aunque todo salvo la clave sea conocido por el atacante.
06 · Menos puertas, mejor cerradas
Código seguro y la superficie de ataque
La criptografía más fuerte es inútil si el firmware tiene un desbordamiento de buffer que permite ejecutar código ajeno: las técnicas de los temas 31 y 39 (validar las entradas, límites siempre comprobados, canario, MPU) son también defensas de seguridad, porque un error de memoria es el primer escalón de un ataque remoto. Además, la seguridad pide una disciplina propia:
| Práctica | Qué previene |
|---|---|
| Reducir la superficie de ataque: deshabilitar los servicios, puertos y comandos que no se usan; sin interfaces de depuración en producción | Cada interfaz abierta es una puerta; la más segura es la que no existe |
| Validar toda entrada externa (tema 39): longitud, rango, formato, coherencia con el estado | Desbordamientos y comandos malformados, el ataque remoto más común |
| Mínimo privilegio: cada módulo accede solo a lo que necesita (MPU, TrustZone) | Que un error en un módulo comprometa a todo el sistema |
| Autenticar cada interfaz de control: el cargador por cable, el puerto de configuración, la comunicación con el servidor | Que cualquiera con acceso al conector tome el control |
| Comunicaciones autenticadas y cifradas (TLS), verificando el certificado del servidor | Interceptación y suplantación del servidor, incluido el de actualizaciones |
| No registrar secretos: ni claves ni contraseñas en los registros ni en la caja negra (tema 37) | Fugas por los canales de diagnóstico |
| Credenciales únicas por equipo y sin valores de fábrica | Compromiso de la flota por un solo secreto, o por credenciales conocidas |
| Fallar de forma segura (tema 39): ante un error en la verificación, no ejecutar ni abrir | Que un fallo de la defensa se convierta en un acceso |
Un caso que sorprende a quienes empiezan: TLS necesita saber la hora. Para verificar el certificado de un servidor hay que comprobar que no venció, y un microcontrolador que arranca sin reloj de tiempo real no sabe qué día es. Hay equipos que resuelven la dificultad no verificando la fecha, o aceptando cualquier certificado, y con eso anulan la protección. La solución es tener una fuente de tiempo confiable (un RTC con batería o un servicio de tiempo autenticado) o validar el certificado por otro medio (fijar el certificado o la clave pública del servidor en el firmware: certificate pinning).
Y el eslabón que muchas veces se olvida es el propio mecanismo de actualización: es el camino más directo para tomar el control de todo el equipo, y por eso es el que más cuidado merece. Una actualización firmada, con verificación en cada arranque y protección contra el retroceso, convierte la mayor debilidad (una puerta que permite cambiar el programa) en la mayor fortaleza (la forma de corregir cualquier defecto de seguridad que se descubra después).
07 · El secreto que sostiene todo
Gestión de claves
Todo el esquema descansa en que la clave privada de firma no se filtre. Si se roba, el atacante puede firmar el firmware que quiera, y todos los equipos lo aceptarán: ninguna política del cargador lo detecta, porque la firma es auténtica (es el último escenario del laboratorio). Por eso la gestión de claves es la parte más importante, y la menos vistosa, de un firmware seguro:
| Práctica | Qué logra |
|---|---|
| La clave privada vive en un HSM (módulo de seguridad de hardware) o en un servicio equivalente, sin salir nunca; firmar es una operación del HSM | Que robar el servidor de compilación no dé la clave |
| Una jerarquía de claves: una clave raíz, guardada fuera de línea, que solo certifica claves de firma de uso diario, que se pueden cambiar | Rotar y revocar las claves de uso diario sin cambiar lo que está grabado en los equipos |
| Varias claves de verificación en el equipo (el cargador acepta, por ejemplo, 2 o 3 hashes de claves públicas) | Poder rotar una clave comprometida: se firma con otra, y la primera se revoca |
| Un proceso de firma con separación de funciones: quien escribe el código no puede firmarlo solo; la firma exige una revisión y una compilación reproducible | Que un empleado o un atacante interno firmen un firmware sin que nadie lo sepa |
| Un plan para el compromiso: qué se hace si la clave se filtra | Poder responder: revocar, actualizar la flota con otra clave, avisar. Sin plan, la única salida es retirar los equipos |
| Claves por equipo (provisionamiento) en lugar de una por flota | Que extraer la clave de un equipo no comprometa a los demás |
El caso más duro es la revocación: un equipo ya fabricado, con una clave pública grabada en un fusible, no puede «olvidar» esa clave si se filtra la privada. Solo las claves previstas de antemano (varios huecos de claves, o una jerarquía) permiten la rotación. Es otra decisión que se toma antes de fabricar, como la estrategia de actualización del tema 42: un producto que sale con una sola clave y sin capacidad de cambiarla, si esa clave se compromete, queda sin remedio.
08 · Atacar tu propio producto
Verificación y diagnóstico
La seguridad se verifica con pruebas negativas: en lugar de comprobar que lo bueno funciona, se intenta hacer lo malo y se comprueba que se rechaza. Cada una de estas pruebas debe fallar de la manera prevista:
| Prueba | Cómo | Resultado esperado |
|---|---|---|
| Imagen sin firma | Intentar instalar y arrancar una imagen sin firma | El cargador la rechaza; el equipo sigue con la anterior |
| Imagen modificada | Cambiar un byte del código de una imagen firmada; y repetirlo corrigiendo el CRC | La firma no verifica; el CRC corregido no engaña al cargador |
| Firma de otra clave | Firmar con una clave que no es la del fabricante | Rechazada |
| Retroceso | Instalar una imagen firmada con una versión de seguridad menor que la del contador | Rechazada, con un motivo registrado |
| Encabezado mentiroso | Tamaños imposibles, direcciones fuera de rango, campos truncados (el analizador del encabezado es código que procesa datos externos: tema 31) | Rechazo sin accesos fuera de rango; el analizador se prueba con entradas al azar bajo sanitizers |
| Modificación de la flash en reposo | Cambiar la imagen con un programador y arrancar | El arranque seguro la rechaza (verificación en cada arranque) |
| Estado de las protecciones en producción | Leer el nivel de protección de lectura y el estado del puerto de depuración en unidades de producción, al azar | Protección activa, depuración cerrada; ninguna unidad con el puerto abierto |
| Credenciales | Buscar claves, contraseñas y certificados dentro del binario del firmware (con strings) | No hay ninguno; cada equipo tiene credenciales propias |
| Superficie de ataque | Escanear los puertos y servicios abiertos de un equipo en producción | Solo los necesarios, todos autenticados |
| Síntoma | Causa probable | Qué hacer |
|---|---|---|
| El cargador acepta una imagen modificada con un CRC correcto | Solo se verifica el CRC, que no autentica | Verificar una firma sobre el hash de la imagen |
| El equipo ejecuta una imagen alterada con un programador | Se verificó al instalar pero no en cada arranque, o la flash no está protegida | Arranque seguro en cada arranque; protección de la flash |
| Se puede volver a una versión vieja con una vulnerabilidad | No hay versión de seguridad ni contador monótono | Versión de seguridad firmada y contador en OTP |
| Un atacante firma imágenes tras extraer una clave de un equipo | Se usa una clave compartida (HMAC) o la clave privada está en los equipos | Firma asimétrica: el equipo solo tiene la clave pública |
| La verificación de la firma «siempre da bien» | Se ignora el resultado, se compara mal, o la clave pública puede reemplazarse | Pruebas negativas; clave pública en una zona protegida; falla segura |
| El equipo en producción se puede leer por SWD | La protección de lectura no se activó en la línea de producción | Activarla como paso obligatorio de la producción y verificar una muestra |
| El arranque tarda demasiado tras agregar la firma | El hash y la firma en software de una imagen grande, con un reloj lento | Acelerador de hash por hardware; verificar solo lo necesario; subir el reloj durante la verificación |
| No se puede actualizar una flota tras comprometerse una clave | La clave estaba grabada en fusibles sin previsión de rotación | Varias claves de verificación y una jerarquía, desde el diseño |
09 · Analogía con programación
Probar el modelo en Python
El programa reúne cinco demostraciones: la falsificación de un CRC-32 (el atacante modifica el código y calcula cuatro bytes de relleno que restauran el CRC original); el efecto avalancha de SHA-256 al cambiar un solo bit; la comparación entre un HMAC con clave compartida y una firma de juguete con clave pública; una política de versión de seguridad con contador monótono; y el costo de verificar una imagen en cada arranque con supuestos explícitos.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
# Firmware seguro: un CRC se falsifica, un hash no; HMAC contra firma; la política contra el retroceso; y cuánto cuesta verificar en cada arranque.
import zlib, hashlib, hmac, random
# ---- Parte 1: falsificar un CRC-32 --------------------------------------------------------------------------------------
T = []
for n in range(256):
c = n
for _ in range(8): c = (c >> 1) ^ 0xEDB88320 if c & 1 else c >> 1
T.append(c)
INDICE = {T[i] >> 24: i for i in range(256)} # el byte alto de cada entrada de la tabla es único
def forjar(datos, objetivo):
"""Devuelve 4 bytes que, agregados a 'datos', hacen que su CRC-32 valga 'objetivo'."""
s = zlib.crc32(datos) ^ 0xFFFFFFFF # estado interno tras procesar 'datos'
x, indices = objetivo ^ 0xFFFFFFFF, []
for _ in range(4): # se recorre el cálculo al revés, un byte por vez
i = INDICE[x >> 24]; indices.append(i)
x = ((x ^ T[i]) << 8) & 0xFFFFFFFF
parche = bytearray()
for i in reversed(indices): # y se recorre hacia adelante para hallar los bytes
parche.append((s & 0xFF) ^ i)
s = (s >> 8) ^ T[i]
return bytes(parche)
rnd = random.Random(7)
original = bytes(rnd.randrange(256) for _ in range(4096)) # el firmware legítimo
malicioso = bytearray(original); malicioso[100:104] = b"EVIL" # el atacante cambia código
crc_legitimo = zlib.crc32(original)
parche = forjar(bytes(malicioso), crc_legitimo)
malicioso_forjado = bytes(malicioso) + parche
print("Parte 1: el atacante modifica el firmware y corrige el CRC con 4 bytes de relleno")
print(f" CRC-32 del firmware legítimo : {crc_legitimo:#010x}")
print(f" CRC-32 sin corregir : {zlib.crc32(bytes(malicioso)):#010x} (el cargador lo rechazaría)")
print(f" CRC-32 del malicioso forjado : {zlib.crc32(malicioso_forjado):#010x} (bytes de relleno: {parche.hex()}); coincide: {zlib.crc32(malicioso_forjado) == crc_legitimo}")
print(f" SHA-256 del legítimo : {hashlib.sha256(original).hexdigest()[:32]}...")
print(f" SHA-256 del malicioso : {hashlib.sha256(malicioso_forjado).hexdigest()[:32]}... (no se puede forjar a voluntad)")
# ---- Parte 2: el efecto avalancha de un hash -------------------------------------------------------------------------------
base = hashlib.sha256(original).digest()
cambios = []
for k in range(1000):
d = bytearray(original); bit = rnd.randrange(len(d) * 8); d[bit // 8] ^= 1 << (bit % 8)
h = hashlib.sha256(bytes(d)).digest()
cambios.append(sum(bin(a ^ b).count("1") for a, b in zip(base, h)))
print()
print("Parte 2: cambiar 1 solo bit de una imagen de 4 KB, 1000 veces")
print(f" bits distintos del SHA-256 (de 256): mínimo {min(cambios)}, promedio {sum(cambios) / len(cambios):.1f}, máximo {max(cambios)}")
print(" un hash se comporta como un sorteo: no hay forma de elegir qué cambios producen un hash dado (para un CRC, sí: Parte 1)")
# ---- Parte 3: HMAC (clave compartida) frente a una firma (clave privada y pública) -----------------------------------------
clave_flota = b"clave-de-toda-la-flota"
tag = hmac.new(clave_flota, original, hashlib.sha256).digest()
print()
print("Parte 3: autenticar una imagen")
print(f" HMAC con la clave de la flota: verifica la imagen original: {hmac.compare_digest(tag, hmac.new(clave_flota, original, hashlib.sha256).digest())}")
print(f" el mismo HMAC sobre la imagen maliciosa: {hmac.compare_digest(tag, hmac.new(clave_flota, malicioso_forjado, hashlib.sha256).digest())}")
extraida = clave_flota # alguien extrae la clave de UN solo equipo
tag_falso = hmac.new(extraida, malicioso_forjado, hashlib.sha256).digest()
print(f" con la clave extraída de un equipo, el atacante firma lo que quiera y el HMAC verifica: {hmac.compare_digest(tag_falso, hmac.new(clave_flota, malicioso_forjado, hashlib.sha256).digest())}")
print(" (el equipo necesita la misma clave para verificar que la que se usa para firmar: robar un equipo compromete toda la flota)")
# Firma de juguete (RSA con números chicos, SOLO para ver el mecanismo; no es seguro):
p, q = 2147483647, 2147483629
n, e = p * q, 65537
d = pow(e, -1, (p - 1) * (q - 1)) # la clave privada: solo la tiene quien firma
def firmar(img): return pow(int.from_bytes(hashlib.sha256(img).digest(), "big") % n, d, n)
def verificar(img, firma): return pow(firma, e, n) == int.from_bytes(hashlib.sha256(img).digest(), "big") % n
firma = firmar(original)
print(f" firma de juguete (RSA de {n.bit_length()} bits): el equipo guarda solo la clave PÚBLICA (n, e={e}); verifica el original: {verificar(original, firma)}")
print(f" verifica la imagen maliciosa con esa firma: {verificar(malicioso_forjado, firma)}; con una firma inventada por el atacante: {verificar(malicioso_forjado, 123456789)}")
print(" la clave pública sirve para VERIFICAR pero no para FIRMAR: robar un equipo no permite firmar imágenes (en la práctica: ECDSA o Ed25519, claves de 256 bits o RSA de 2048 o más)")
# ---- Parte 4: política contra el retroceso ----------------------------------------------------------------------------------
print()
print("Parte 4: versión de seguridad frente a un contador monótono guardado en el equipo (fusibles u OTP)")
contador, instalada = 1, ("1.0", 1)
eventos = (("instalar 1.1 (seguridad 1)", ("1.1", 1)), ("instalar 2.0 (seguridad 2, corrige una vulnerabilidad)", ("2.0", 2)),
("el atacante instala la 1.1, FIRMADA y legítima, para explotar la vulnerabilidad", ("1.1", 1)),
("instalar 2.1 (seguridad 2)", ("2.1", 2)), ("la 2.1 no pasa la prueba: volver atrás a la 2.0 (seguridad 2)", ("2.0", 2)), ("volver atrás a la 1.1", ("1.1", 1)))
for texto, (ver, seg) in eventos:
if seg < contador: decision = f"RECHAZADA (seguridad {seg} < contador {contador})"
else:
decision = "aceptada"; instalada = (ver, seg)
if seg > contador: contador = seg; decision += f"; tras confirmar, el contador sube a {contador}"
print(f" {texto:<80} -> {decision}")
# ---- Parte 5: cuánto cuesta verificar en cada arranque ---------------------------------------------------------------------
print()
print("Parte 5: costo aproximado de verificar una imagen de 256 KB en cada arranque (SUPUESTOS: 25 ciclos por byte para SHA-256 en software; 15 millones de ciclos para verificar una firma ECDSA P-256)")
for mhz in (48, 80, 168):
sha = 256 * 1024 * 25 / (mhz * 1e6) * 1000
ecdsa = 15e6 / (mhz * 1e6) * 1000
print(f" núcleo a {mhz:>3} MHz: hash {sha:6.1f} ms + firma {ecdsa:6.1f} ms = {sha + ecdsa:6.1f} ms por arranque")
Resultado, parte 1: el firmware legítimo tiene el CRC-32 0x09990583; al cambiar 4 bytes, el CRC pasa a 0x0dac6e0a y el cargador del tema 42 lo rechazaría; pero con 4 bytes de relleno (1d6a8fb6), el CRC de la imagen maliciosa vuelve a ser 0x09990583: coincide. El SHA-256 de ambas es totalmente distinto. Parte 2: al cambiar 1 bit de una imagen de 4 KB, 1000 veces, el SHA-256 cambia en promedio 128,1 de sus 256 bits (de 104 a 153). Parte 3: el HMAC con la clave de la flota verifica la imagen original y rechaza la maliciosa, pero con la clave extraída de un solo equipo el atacante firma lo que quiere y verifica; con una firma de juguete (RSA de 62 bits) el equipo solo guarda la clave pública y verifica el original, y rechaza la imagen maliciosa y una firma inventada. Parte 4: con el contador en 1, se aceptan la 1.1 y la 2.0 (que sube el contador a 2); la 1.1 firmada que instala el atacante se rechaza (1 < 2); se acepta la 2.1 y la vuelta atrás a la 2.0 (la vuelta atrás legítima sigue funcionando); y volver a la 1.1 se rechaza. Parte 5: verificar 256 KB cuesta unos 449 ms a 48 MHz (136,5 de hash y 312,5 de firma), 269 ms a 80 MHz y 128 ms a 168 MHz.
Probá cambiar el objetivo del CRC en la Parte 1 a cualquier valor (forjar(datos, 0xDEADBEEF)) y verificá que siempre se puede lograr, o hacé que el atacante cambie el firmware en una posición distinta. La lección del programa es la distinción central del tema: un CRC protege contra la mala suerte, y solo una firma protege contra la mala intención. Y el RSA de juguete es solo para ver el mecanismo: con 62 bits se factoriza en segundos; en la práctica se usan curvas elípticas de 256 bits o RSA de 2048 bits o más, siempre con una biblioteca probada.
10 · Exploración interactiva
Tres políticas de arranque
El laboratorio compara tres políticas de un cargador frente a seis imágenes: una legítima, una dañada por ruido, una maliciosa con el CRC corregido, una vieja con una vulnerabilidad pero firmada, una firmada con otra clave y una firmada con la clave robada del fabricante. Las políticas son solo CRC (el cargador del tema 42), CRC + firma y CRC + firma + anti-retroceso. El equipo ya tiene el contador de seguridad en 2. Las celdas en rojo son errores: una imagen maliciosa aceptada, o una legítima rechazada.
Qué imágenes acepta cada política
Decisión de cada política para la imagen elegida
—
Elegí una imagen.
Recorrido guiado
- Elegí la imagen 1 (legítima y reciente): las tres políticas la aceptan. Es el comportamiento correcto, y la referencia para comparar las demás.
- Elegí la imagen 2 (dañada por ruido): las tres la rechazan, hasta la de solo CRC. Es el caso para el que sirve el CRC, y por eso parece suficiente cuando uno solo prueba con errores accidentales.
- Elegí la imagen 3 (maliciosa con el CRC corregido): la política de solo CRC la acepta (celda roja) y las otras dos la rechazan porque la firma no verifica. Es la demostración de que el CRC no autentica.
- Elegí la imagen 4 (vieja, firmada, con una vulnerabilidad): solo la política con anti-retroceso la rechaza. «CRC + firma» la acepta, porque la firma es auténtica; la versión de seguridad 1 es menor que el contador 2.
- Elegí la imagen 5 (firmada por el atacante con su propia clave): el CRC solo la acepta; las que verifican firma la rechazan, porque la clave no es la que el equipo tiene como confiable.
- Elegí la imagen 6 (firmada con la clave robada del fabricante): las tres políticas la aceptan. Ningún mecanismo del cargador puede detectarla, porque la firma es auténtica. La defensa está fuera del cargador: proteger la clave en un HSM, poder revocarla y rotarla, y detectar el daño con el despliegue escalonado (tema 42).
Desafío: ¿cómo se limitaría el daño de una clave de firma comprometida en equipos ya fabricados?
Depende de lo que se haya previsto antes de fabricar. Si el cargador acepta varias claves de verificación (por ejemplo, tres hashes de claves públicas en los fusibles), se puede firmar una actualización con una clave de reserva y, en esa misma actualización, revocar la comprometida (por ejemplo, subiendo un contador de claves o quemando el fusible que la deshabilita). Desde ese momento, el equipo ya no acepta imágenes firmadas con la clave robada, aunque la firma sea auténtica. Con una jerarquía (una raíz fuera de línea que certifica claves de uso diario), se revoca la clave de uso diario y se emite una nueva, sin tocar lo grabado en los equipos. El problema es el período entre el robo y la revocación: el atacante puede firmar y distribuir una imagen maliciosa, y solo los equipos que todavía no la hayan recibido se salvan. Y si no se previó nada (una sola clave, grabada en un fusible), la revocación es imposible: la única salida es retirar los equipos. Por eso la rotación de claves se diseña antes del primer equipo, igual que la estrategia de actualización.
11 · Método de trabajo
Método: diseñar con un enemigo
- Hacé el modelo de amenazas: activos, atacantes (remoto, cercano, físico, cadena de suministro) y vectores. Decidí el nivel de protección en proporción a lo que se defiende.
- Eliminá primero lo evidente: sin credenciales por defecto, sin claves en el código, sin interfaces que no se usan, sin puertos de depuración abiertos en producción.
- Distinguí integridad de autenticidad: el CRC para el ruido, una firma para el atacante. Verificá la firma de toda imagen, y con un hash criptográfico, nunca con un CRC.
- Usá firma asimétrica: el equipo solo tiene la clave pública. Con una biblioteca probada (MCUboot, mbed TLS, TinyCrypt), nunca criptografía propia.
- Armá la cadena de confianza: una raíz inmutable (ROM y fusibles) que verifica el cargador, que verifica la aplicación, en cada arranque; con la clave pública en una zona protegida.
- Agregá protección contra el retroceso: versión de seguridad firmada y contador monótono; subirla solo al corregir vulnerabilidades y tras confirmar.
- Protegé el contenido: protección de lectura y cierre del puerto de depuración en producción, cifrado de la flash y de las actualizaciones si la propiedad intelectual lo justifica, y un elemento seguro si hay claves de valor.
- Hacé que cada equipo sea único: credenciales por equipo, provisionadas en fábrica, y un generador aleatorio por hardware.
- Gestioná las claves como un activo crítico: clave privada en un HSM, jerarquía, varias claves de verificación para poder rotar, separación de funciones en la firma y un plan para el compromiso.
- Probá atacando: imágenes sin firma, modificadas, con CRC corregido, de otra clave y viejas; encabezados mentirosos; flash modificada en reposo; y verificá el estado de las protecciones en unidades de producción.
12 · Comprensión
Confusiones frecuentes
«Con un CRC en la imagen, nadie puede instalar un firmware modificado»
El CRC se calcula sin ningún secreto, y se puede falsificar en un instante: con cuatro bytes de relleno se logra cualquier CRC-32 deseado. Sirve contra el ruido, no contra una persona. Para detener una modificación intencional hace falta una firma digital.
«Un hash (SHA-256) autentica la imagen»
Garantiza que la imagen no cambió respecto de ese hash, pero cualquiera puede calcular el hash de su propia imagen y reemplazar el valor. El hash solo sirve si el valor viaja protegido, y esa protección es la firma (o un HMAC).
«El HMAC es una firma más simple, y me alcanza»
Exige que la clave esté dentro de cada equipo. Quien extraiga la clave de un solo equipo puede firmar para toda la flota. La firma con clave pública guarda en el equipo solo lo necesario para verificar, y robar un equipo no da con qué falsificar.
«Si la firma es válida, la imagen es buena»
La firma prueba quién la hizo y que no se modificó, no que sea reciente (una versión vieja y vulnerable tiene firma válida) ni que no tenga defectos, ni que la clave no se haya filtrado. Hacen falta protección contra el retroceso, arranque de prueba con vuelta atrás y gestión de claves.
«Verifico la firma al instalar; ya está»
Un atacante con acceso físico puede modificar la flash después, por el puerto de depuración o con un programador. Se verifica en cada arranque, antes de ejecutar, y se protege la flash y la clave pública contra escritura.
«Escribo mi propio algoritmo de cifrado, así nadie sabe cómo funciona»
La seguridad por oscuridad no existe: la criptografía casera casi siempre tiene fallos que un especialista encuentra en horas. Se usan algoritmos públicos y estudiados por años (AES, SHA-256, ECDSA, Ed25519), en bibliotecas probadas, y la seguridad descansa en la clave, que es lo único secreto.
«Una clave en el firmware, bien escondida, es suficiente»
Está en cada copia del firmware, y alguien con un programador puede leerla. Si es común a la flota, comprometer un equipo compromete todos. Las claves viven en hardware protegido (un elemento seguro, TrustZone, flash protegida) y cada equipo tiene la suya.
«Activar la protección de lectura al final es opcional»
Con el puerto de depuración abierto, cualquiera con un cable lee el código, las claves y los datos, y clona el producto. La protección se activa como un paso obligatorio de la producción, y se verifica en una muestra de las unidades. (Y el nivel máximo suele ser irreversible: se aplica en producción, no durante el desarrollo.)
13 · Práctica guiada
Ejercicios
1. Fuerza bruta
Un atacante quiere encontrar una imagen maliciosa con un SHA-256 determinado probando al azar, a razón de 1012 intentos por segundo. ¿Cuántos años tarda en probar todas las posibilidades (2256)? ¿Y para un CRC-32 (232 posibilidades), si no supiera la técnica de los 4 bytes?
Ver solución
2256 ≈ 1,16 × 1077; dividido por 1012 intentos por segundo: 1,16 × 1065 s; un año tiene unos 3,16 × 107 s, de modo que son 3,7 × 1057 años (la edad del universo es de 1,4 × 1010): inviable. Para el CRC-32: 232 ≈ 4,3 × 109 intentos / 1012 = 0,004 segundos. Pero ni siquiera hace falta probar: con la técnica de la Parte 1 se calculan 4 bytes de relleno en forma directa, sin ninguna búsqueda. Esa es la diferencia entre una función hecha para detectar errores y una hecha para resistir a un adversario.
2. Presupuesto de arranque
Una imagen de 128 KB se verifica en cada arranque con un núcleo a 48 MHz, con 25 ciclos por byte para el hash y 15 millones de ciclos para la firma. ¿Cuánto tarda cada parte y el total? Si el requisito es que el equipo arranque en menos de 400 ms, ¿se cumple?
Ver solución
Hash: 128 × 1024 × 25 = 3 276 800 ciclos / 48 MHz = 68,3 ms. Firma: 15 000 000 / 48 MHz = 312,5 ms. Total: 380,8 ms: se cumple, con apenas un 5 % de margen (y sin contar el resto del arranque). Si el margen es insuficiente: un acelerador de hash por hardware (reduce los 68 ms a unos pocos), subir el reloj durante la verificación (a 80 MHz, la firma baja a 187 ms), una verificación más barata de la firma (Ed25519 suele ser más rápido que ECDSA en software) o, en un chip con la flash protegida, verificar la firma solo tras una actualización y el hash en cada arranque (lo que hay que justificar con el modelo de amenazas).
3. El contador de seguridad
El contador del equipo vale 3. Llegan, firmadas por el fabricante, imágenes con estas versiones de seguridad: A (3), B (2), C (4). ¿Cuáles acepta? Si instala y confirma la C, ¿qué pasa después con la A?
Ver solución
La regla es aceptar solo si la versión de seguridad es mayor o igual que el contador (3). A (3): aceptada. B (2): rechazada. C (4): aceptada. Si se instala y confirma la C, el contador sube a 4, y la A (3) pasa a ser rechazada (3 < 4): la vuelta atrás a ella ya no es posible. Por eso la versión de seguridad se sube solo cuando es imprescindible (se corrigió una vulnerabilidad real), y se confirma antes de subir el contador; subirla por cada versión cerraría la vuelta atrás del tema 42 sin necesidad.
4. Robar un equipo
Un atacante extrae las claves de un solo equipo de una flota de 100 000. ¿Qué puede hacer si la flota usa: (a) un HMAC con una clave común, (b) un HMAC con una clave distinta por equipo, (c) una firma con clave pública en el equipo?
Ver solución
(a) Compromete la flota entera: con la clave común firma imágenes que los 100 000 equipos aceptan. (b) Compromete solo ese equipo: la clave de ese equipo solo verifica etiquetas para él; el fabricante debe guardar o derivar las 100 000 claves, y firmar una etiqueta distinta por equipo, lo que complica la distribución de una misma imagen. (c) No compromete nada respecto de las imágenes: el atacante solo obtiene la clave pública, que no permite firmar. Todavía puede controlar ese equipo, pero no a los demás. Es la razón de que la firma asimétrica sea la opción para flotas, y de que las credenciales (los secretos que sí hay que guardar en el equipo, como su identidad ante el servidor) sean únicas.
5. Modelo de amenazas
Armá, en pocas líneas, el modelo de amenazas de dos productos: (a) un sensor de temperatura de un depósito, alimentado por pilas, que envía datos por LoRa; (b) la cerradura con teclado del tema 27 en una oficina. ¿Cuál es el activo principal, el atacante más probable y la defensa que no puede faltar?
Ver solución
(a) Sensor: activo principal, la integridad de los datos y que el equipo no sea una plataforma de ataque; atacante probable, uno remoto o cercano por el enlace de radio (inyectar mensajes falsos), y alguien que lo encuentre en el campo; defensa imprescindible: mensajes autenticados (con una clave por equipo) y actualización firmada; la protección física pesa poco (el equipo vale poco y el firmware no es secreto). (b) Cerradura: activo principal, la función (que no se abra sin autorización) y el código de acceso; atacante probable, uno físico (alguien con acceso al teclado y a la placa) además de la fuerza bruta (tema 27); defensas imprescindibles: protección de lectura y cierre del puerto de depuración, código guardado de forma segura (con CRC y copias, y mejor aún con un hash), bloqueo por intentos persistente (tema 27 y 40), arranque seguro, y una caja que impida el acceso al conector. Los dos necesitan sin credenciales por defecto y poder actualizarse; lo que cambia es dónde poner el esfuerzo.
6. El momento de la verificación
Un cargador verifica la firma de la imagen solo al recibirla por la red. Un atacante con acceso físico al equipo reescribe la flash con un programador, cambiando una instrucción. ¿Qué pasa? ¿Qué dos cambios lo evitan?
Ver solución
El equipo arranca la imagen modificada sin darse cuenta: la verificación ocurrió antes, cuando la imagen era la legítima, y nada la repite. Dos cambios lo evitan, que se complementan: (1) verificar la firma en cada arranque, antes de ejecutar (arranque seguro), de modo que cualquier modificación rompa la firma y el equipo se niegue a ejecutarla; y (2) proteger la flash y el puerto de depuración (protección de lectura y escritura, depuración cerrada) para que modificarla sea mucho más difícil. Ninguna de las dos sola es suficiente: sin la (2), un atacante de recursos podría además reemplazar el cargador o la clave pública; sin la (1), cualquier acceso a la flash es un control total.
14 · Síntesis del bloque
Ideas para recordar
- La seguridad se diseña contra amenazas concretas: activos (firmware, propiedad intelectual, claves, datos, función), atacantes (remoto, cercano, físico, cadena de suministro) y vectores. Es una cuestión de costo: se trata de que atacar valga menos que defender. Las vulnerabilidades más explotadas son las más tontas (credenciales por defecto, Mirai).
- Integridad no es autenticidad. Un CRC detecta el ruido pero se falsifica en un instante (4 bytes de relleno dan cualquier CRC-32); un hash criptográfico no se puede forjar a voluntad (un bit cambia 128 de 256 bits) pero no autentica por sí solo; un HMAC necesita una clave compartida dentro de cada equipo (robar uno compromete la flota); una firma digital deja en el equipo solo la clave pública, que verifica pero no firma.
- El arranque seguro verifica la firma en cada arranque, antes de ejecutar, con una cadena de confianza que nace en lo inmutable (ROM y fusibles OTP) y llega a la aplicación. La clave pública vive en una zona protegida; la comparación es en tiempo constante; verificar cuesta unos 130 a 450 ms según el reloj. Se usa MCUboot u otra biblioteca probada, nunca criptografía propia.
- La protección contra el retroceso usa una versión de seguridad firmada y un contador monótono en OTP: se rechaza una imagen vieja aunque la firma sea auténtica. Se separa de la versión de funciones y se sube solo al corregir una vulnerabilidad y tras confirmar, para no cerrar la vuelta atrás del tema 42 sin necesidad.
- Para proteger el contenido: protección de lectura y cierre del puerto de depuración en producción, cifrado de la flash y de las actualizaciones, elemento seguro para las claves de valor, TrustZone para aislar lo sensible. Credenciales únicas por equipo, nunca claves en el código, un generador aleatorio por hardware, borrado seguro. La ofuscación no es seguridad.
- El código seguro reduce la superficie de ataque, valida cada entrada, aplica mínimo privilegio, autentica cada interfaz y usa comunicaciones autenticadas (TLS necesita conocer la hora). Los errores de memoria de los temas 31 y 39 son también defectos de seguridad. El mecanismo de actualización es el eslabón más importante: bien hecho, es la defensa; mal hecho, es la puerta.
- La gestión de claves es el sostén de todo: clave privada en un HSM, una jerarquía, varias claves de verificación para poder rotar y revocar, separación de funciones en la firma y un plan para el compromiso. Una clave de firma robada rompe todas las políticas del cargador: la rotación se diseña antes de fabricar el primer equipo.
- El bloque en conjunto: guardar los datos de modo que sobrevivan a los cortes (40), registrar lo que no cabe adentro del chip (41), actualizar el firmware sin riesgo de dejar el equipo inutilizado (42) y demostrar que lo que se instala y se ejecuta es lo que el fabricante firmó (43). Se prueba atacando: cada intento malicioso debe ser rechazado de la forma prevista, y el estado de las protecciones se verifica en las unidades de producción.
¿Listo para el bloque siguiente?
Antes de avanzar, hacé el modelo de amenazas de un producto tuyo en una hoja: tres activos, los atacantes posibles y la defensa más barata para cada combinación. Después respondé: ¿el cargador acepta una imagen con un CRC correcto y nada más?, ¿se puede volver a una versión vieja?, ¿el puerto de depuración está cerrado en producción?, ¿hay alguna clave o contraseña dentro del binario? Cada «sí» a la primera, tercera o cuarta es una puerta abierta.
En el próximo bloque pasamos de construir a encontrar los errores: la depuración y las pruebas. Empezamos con la depuración básica: LEDs, pines de prueba, salida por consola y registro de eventos, las herramientas que se usan cuando el equipo no hace lo que debe y no hay un depurador a mano.