← Índice del curso

Internet de las Cosas · Tema 44

Actualizaciones seguras y reversión

El archivo llegó completo y el equipo arrancó. Todavía faltan dos preguntas: ¿ese firmware fue autorizado y funciona lo suficiente para quedarse? Vamos a vincular firmas, particiones y pruebas de salud con una recuperación que también respete el nivel de seguridad y los datos persistidos.

01 · Completar la operación OTA

Autorización y aceptación operativa

El tema 43 llevó una imagen hasta su primer arranque. Ahora distinguimos la autorización del código de su aceptación operativa. Una imagen legítimamente firmada puede contener un error; una imagen que funciona puede provenir de una fuente no autorizada. Se necesitan ambos controles.

Comprobaciones que resuelven problemas diferentes.
ControlPreguntaLímite
Integridad¿El contenido coincide con el esperado?No identifica por sí sola quién lo autorizó
Firma y política de claves¿Lo autorizó una clave aceptada para ese propósito?No demuestra ausencia de errores
Compatibilidad¿Corresponde a este hardware y sus dependencias?No prueba salud de esta unidad
Diagnóstico de arranque¿Satisface los criterios locales de aceptación?No detecta necesariamente errores tardíos
Reversión¿Hay una aplicación anterior que todavía pueda operar?Depende de imágenes, datos y política de seguridad

La disponibilidad de dos copias de firmware ayuda, pero no garantiza una recuperación completa. El respaldo debe seguir siendo auténtico, elegible y compatible con los datos que encontrará. Además, el hardware mantiene las condiciones seguras del producto durante el reinicio y la recuperación.

El laboratorio empieza con una imagen ya descargada. Simula las decisiones posteriores y el estado de dos slots. No implementa algoritmos criptográficos, no modifica flash y no programa eFuses.

02 · Autorizar el artefacto correcto

Qué garantiza una firma

La firma digital vincula el contenido firmado con una clave privada. El verificador comprueba la firma mediante una clave pública aceptada y las reglas del esquema. No se considera confiable una clave solo porque venga incluida junto con el binario que quiere autorizar.

Artefacto + firma + clave pública aceptada + política
→ comprobación de autenticidad e integridad del contenido cubierto

La clave privada de publicación se protege fuera de la flota. Los dispositivos necesitan el material de verificación correspondiente; no necesitan la clave que permite firmar versiones. Tampoco se reutiliza la credencial operativa de equipo07 como autoridad de publicación de firmware.

Qué contenido queda cubierto

Se verifica el formato exacto que define la plataforma: imagen, cabeceras, relleno o bloque de firma según corresponda. No se inventa un protocolo concatenando una contraseña con el archivo y calculando un hash. La biblioteca y las herramientas oficiales realizan la comprobación criptográfica.

La política también vincula el artefacto con su producto, dependencias y restricciones. Si un manifiesto decide el destino o la aplicabilidad, sus campos críticos necesitan protección y relación inequívoca con la imagen. Firmar un binario y aceptar metadatos modificables que lo asignen a cualquier placa deja decisiones importantes sin proteger.

RFC 9124 describe un modelo de información para manifiestos, incluyendo identidad, condiciones y secuencia. Es una referencia para diseñar la información protegida; los perfiles de esta lección son una simplificación propia.

Firma válida, versión no admitida

Una imagen antigua puede conservar una firma correcta y tener una vulnerabilidad conocida. La firma demuestra una autorización bajo una política de claves, no que sea la publicación que corresponde instalar hoy. Se necesitan controles de versión, secuencia y nivel mínimo según el producto.

La firma tampoco es cifrado: el contenido firmado puede seguir siendo público. Confidencialidad de distribución, autenticidad y protección del almacenamiento son objetivos diferentes.

03 · Desde una raíz aceptada hasta la aplicación

Claves y cadena de arranque

Verificar al recibir una actualización protege esa ruta de instalación. La verificación durante el arranque busca que el código ejecutado respete la cadena de confianza prevista. Si alguien puede reemplazar el verificador o el material que decide qué aceptar, una firma del archivo no resuelve por sí sola ese escenario.

En plataformas compatibles, Secure Boot v2 de Espressif verifica componentes de arranque y aplicaciones firmadas. El soporte y sus condiciones dependen del chip y revisión; la documentación para ESP32 clásico especifica revisión 3.0 o posterior.

La misma documentación distingue verificación de aplicaciones firmadas durante OTA sin Secure Boot por hardware. Ese modo no ofrece la misma protección frente a un atacante que puede escribir flash. Se elige el mecanismo según el escenario y las capacidades reales.

Cadena de confianza

Define qué componente verifica al siguiente, dónde reside la autoridad y cómo se impide sustituirla por material arbitrario.

Salud operativa

Define qué debe funcionar después de arrancar y qué se hace si falla. Un boot autenticado todavía puede ejecutar una versión defectuosa.

Producción y mantenimiento de claves

El proceso de publicación limita quién solicita una firma y para qué producto. Conserva evidencia del artefacto aprobado y separa las tareas de construcción, aprobación y acceso a la clave cuando la operación lo requiere.

También contempla pérdida o compromiso de una clave. La rotación y revocación de autoridades dependen de lo que el hardware y el formato permitan; no se presume que toda placa pueda incorporar claves nuevas sin límites. Se verifica que las imágenes de recuperación sigan aceptándose después de un cambio.

Las protecciones de fabricación y su configuración se ensayan en el entorno previsto antes de desplegar. Esta lección no incluye comandos para programar eFuses ni cambiar interfaces de recuperación.

04 · Imágenes y estado son cosas distintas

Particiones y metadatos

Un esquema de dos slots conserva una aplicación conocida mientras prepara otra en el destino inactivo. Los metadatos indican qué se debe arrancar y qué estado tiene cada imagen. Esos metadatos también necesitan consistencia frente a cortes.

Dos slots y una fase de pruebaEl slot A contiene una aplicación confirmada. B recibe un candidato comprobado y arranca en prueba. Si supera diagnóstico y confirmación queda aceptado; si falla se intenta volver a A, siempre que siga siendo elegible y compatible con los datos.A: versión confirmadase conserva como respaldoB: candidato en pruebatodavía sin aceptaciónDiagnóstico satisfactorioy confirmación durableFallo o reset sin confirmarbuscar respaldo elegible

La tabla de particiones de ESP-IDF describe regiones y subtipos. Este CSV ilustrativo reserva dos slots de 1,5 MiB en una flash de 4 MiB, suponiendo la tabla en 0x8000:

# Name,   Type, SubType, Offset,   Size
nvs,      data, nvs,     0x9000,   0x6000
otadata,  data, ota,     0xf000,   0x2000
phy_init, data, phy,     0x11000,  0x1000
ota_0,    app,  ota_0,   0x20000,  0x180000
ota_1,    app,  ota_1,   0x1a0000, 0x180000

No es una configuración universal. Se valida tamaño de flash, alineación, tamaño final firmado, bootloader y todas las regiones del producto. Si cambia el offset de la tabla, se recalcula el mapa completo. El archivo no incluye una partición factory ni prescribe activar anti-rollback.

Qué protege el respaldo

No se usa el slot de la aplicación que está ejecutándose como destino de descarga. Tampoco se borra el respaldo para ganar espacio mientras el candidato sigue en prueba. Con dos slots, comenzar la siguiente actualización reutiliza uno: el diseño debe saber qué copia deja de conservar.

El laboratorio abstrae tamaños, borrado y escritura como una transición de instalación. El tema 43 desarrolla transferencia y capacidad. Aquí interesa que una imagen nueva, una pendiente y una válida tengan significados diferentes.

05 · Evidencia antes de aceptar

Primer arranque y salud

El primer arranque inicia una etapa acotada de prueba. La aplicación verifica las condiciones esenciales del producto y confirma su aceptación solo cuando tiene evidencia suficiente. Una pantalla de bienvenida o el primer mensaje de log no prueban que el equipo pueda operar.

Criterios de salud que se adaptan a cada producto.
ÁreaEjemplo de evidenciaCuidado
ConfiguraciónEstructura legible, versión y valores válidosNo sustituir automáticamente datos esenciales por valores peligrosos
AlmacenamientoEstado coherente y acceso a los datos necesariosUna prueba debe respetar desgaste y consistencia
Control localTareas dentro de plazo y salidas inicializadas apropiadamenteNo accionar una carga peligrosa para demostrar salud
Periféricos esencialesIdentificación y lecturas admitidas por la políticaDistinguir fallo de firmware de una desconexión física previa
ComunicaciónPila local y contratos requeridos por el productoLa caída de un servicio externo no siempre justifica revertir

Un diagnóstico con plazo

Se define un límite para la prueba y qué hacer ante cada resultado. Un watchdog detecta ciertas faltas de progreso, pero no identifica todos los fallos funcionales. Una tarea puede alimentar al watchdog mientras devuelve mediciones equivocadas.

Si los criterios incluyen un servicio remoto, se contempla que la red esté caída antes de actualizar. Revertir repetidamente por una falla externa puede dejar al equipo en un ciclo sin resolverla. El producto diferencia salud esencial local, comunicación y observación de plataforma.

Confirmación durable

La confirmación cambia la política de arranque. Se comprueba su retorno y no se anuncia aceptación si la persistencia falló. El diagnóstico corresponde al firmware y al arranque actuales; no se recupera un flag de éxito de la versión anterior para confirmar la nueva.

En el modelo, salud resume evidencia local. La acción diagnosticar registra que esa prueba pasó; confirmar vuelve a exigir condiciones válidas y persistencia. No es un campo que la nube pueda marcar para saltar controles del equipo.

06 · Recuperar servicio, no solo cambiar de slot

Volver a una aplicación conocida

La reversión abandona un candidato no aceptado y busca una aplicación anterior admisible. Para ser una recuperación operativa, esa aplicación también debe poder trabajar con la configuración y los datos que quedaron.

Eventos y resultados esperados.
EventoRespuesta prevista
El candidato no supera la comprobación de arranqueNo ejecutar ese candidato; buscar una alternativa admisible
Declara un fallo esencial durante la pruebaRechazarlo y aplicar la ruta de reversión prevista
Se reinicia sin confirmarNo asumir aceptación por haber arrancado una vez
Diagnóstico y aceptación terminan bienConservar el candidato como aplicación confirmada
No queda una aplicación operativa admisibleEntrar en recuperación autorizada y mantener condiciones locales seguras

En ESP-IDF, la reversión de aplicación se configura con CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE. Los estados de OTA distinguen la aplicación nueva, pendiente de verificar y válida. Un reinicio sin aceptación durante la prueba puede descartar el candidato y seleccionar una alternativa.

Eso no crea un respaldo inexistente. Se comprueba que haya otra aplicación arrancable bajo la política vigente. Un binario vacío, corrupto o prohibido por seguridad no sirve como salida de emergencia.

Fallas después de confirmar

La confirmación termina la protección de prueba inicial de ese candidato. No se presume que un error semanas después vaya a activar la misma reversión automáticamente. La respuesta a fallos tardíos puede requerir otra publicación, una acción de gestión autorizada o un procedimiento local de recuperación.

El laboratorio permite declarar fallido únicamente un candidato en prueba. La recuperación es un estado final del ejercicio: no incluye un botón que elimine controles de firma o de seguridad para volver a arrancar.

07 · Evitar volver a una vulnerabilidad conocida

Reversión y anti-rollback

La reversión busca mantener disponibilidad. La protección contra retroceso, o anti-rollback, evita ejecutar versiones con un nivel de seguridad inferior al mínimo aceptado. Pueden convivir si existe un respaldo que satisfaga ambas políticas.

Respaldo operativo = imagen válida
+ nivel de seguridad ≥ mínimo vigente
+ compatibilidad con datos y entorno

El número de build identifica una construcción; el nivel de seguridad representa una política diferente. Se pueden publicar varios builds con el mismo nivel. Un build con número mayor no es admisible si su nivel de seguridad está por debajo del mínimo que exige el equipo.

La política anti-rollback de ESP-IDF usa una versión de seguridad de imagen y un valor del chip. Con esa configuración, confirmar una aplicación puede actualizar el mínimo aceptado. Sus límites y restricciones se verifican para el chip concreto.

Ejemplo de política representado en el laboratorio.
MomentoMínimoA: seguridad 1B: seguridad 2
B todavía en prueba1Puede ser respaldo, con los demás requisitosSe evalúa antes de aceptar
B confirmada y mínimo elevado2Queda por debajo de la políticaEs la aplicación confirmada

El momento del cambio importa

Si se elimina demasiado pronto la posibilidad de ejecutar la versión anterior, un fallo del candidato puede dejar al equipo sin una alternativa útil. Se planifican diagnóstico, aceptación y recuperación antes de avanzar el mínimo. La operación real puede involucrar cambios irreversibles: no es un contador de preferencias que se reduzca al reiniciar.

El modelo actualiza el mínimo al confirmar, nunca lo disminuye. Representa una política habilitada desde el comienzo; no reproduce el almacenamiento físico del chip ni todos los puntos de fallo entre metadatos y eFuses. «Nuevo escenario ficticio» crea otro ejercicio, no revierte un cambio real de hardware.

El control de nivel no reemplaza la autorización de una publicación ni la protección contra repetición del manifiesto. El modelo admite builds no crecientes si cumplen sus otras condiciones; la secuencia y las excepciones de instalación de un producto real requieren su propia política.

08 · Lo que no vuelve atrás con el binario

Datos que sobreviven al firmware

Una actualización puede cambiar configuración, índices, calibración o formatos de registro. Esos datos suelen vivir fuera del slot de aplicación. Volver al código anterior no restaura automáticamente su contenido.

Supongamos que A interpreta el esquema 1 y B necesita el 2. B arranca y sobrescribe la única copia en formato 2. Si después falla, A puede conservar una firma válida y aun así no operar porque ya no entiende los datos.

Estrategias que se evalúan antes de migrar.
EstrategiaQué aportaQué se debe probar
Lectura compatible con ambos formatosPermite coexistencia durante una transiciónCasos antiguos, nuevos y estructuras incompletas
Copia nueva y conmutación versionadaConserva una representación previa durante la pruebaEspacio, cortes y consistencia entre copias
Migración por etapasSepara preparación, uso y retirada del formato anteriorOrden de versiones y unidades que saltean publicaciones
Respaldo y restauración autorizadosOfrece una recuperación explícitaIntegridad, compatibilidad, antigüedad y protección de secretos

Una prueba deliberadamente peligrosa

El perfil «requiere esquema 2» del laboratorio arranca sin poder superar salud mientras los datos sigan en 1. La acción migrar sobrescribe el esquema de la única copia. Si el candidato falla después, la aplicación A del modelo no puede recuperar servicio con esos datos.

La simulación entra entonces en recuperación. Combina elegibilidad de arranque y compatibilidad operativa para mostrar el problema; un bootloader real no tiene por qué entender el esquema de la aplicación. Podría arrancar el binario antiguo y que sea ese código el que descubra que no puede leer su configuración.

La acción no es una recomendación de migración. No modela cortes dentro de una escritura, restauración ni compatibilidad parcial. En el producto se prueban esos casos y se decide qué datos pueden transformarse antes de confirmar y cuáles deben conservarse para una reversión.

09 · Resolver el primer arranque con el SDK

Integración con ESP-IDF

Este fragmento consulta el estado de la aplicación en ejecución y resuelve una prueba pendiente. El diagnóstico local debe haberse realizado con criterios y plazos del producto. El código no realiza OTA, no habilita Secure Boot y no configura por sí mismo la reversión.

#include <stdbool.h>
#include <stddef.h>
#include "esp_err.h"
#include "esp_ota_ops.h"

esp_err_t resolver_primer_arranque(bool diagnostico_local_ok) {
    const esp_partition_t *actual = esp_ota_get_running_partition();
    if (actual == NULL) return ESP_FAIL;

    esp_ota_img_states_t estado;
    esp_err_t err = esp_ota_get_state_partition(actual, &estado);
    if (err != ESP_OK) return err;
    if (estado != ESP_OTA_IMG_PENDING_VERIFY) {
        return ESP_OK; // no hay una prueba pendiente que resolver aquí
    }
    if (diagnostico_local_ok) {
        return esp_ota_mark_app_valid_cancel_rollback();
    }
    if (!esp_ota_check_rollback_is_possible()) {
        return ESP_ERR_OTA_ROLLBACK_FAILED;
    }
    // En éxito, esta llamada inicia la reversión y reinicia.
    return esp_ota_mark_app_invalid_rollback_and_reboot();
}

Las declaraciones y retornos se contrastan con el encabezado oficial esp_ota_ops.h; se usa el equivalente de la versión instalada. El resultado de check_rollback_is_possible se refiere a la política de imágenes del SDK, no demuestra compatibilidad de los datos de aplicación.

Si devuelve un error, el llamador registra el problema y mantiene la conducta local de recuperación. No continúa como si el firmware hubiera sido aceptado. Incluso cuando no hay estado pendiente, ESP_OK en este fragmento solo significa que no hubo una resolución fallida en esa función; no constituye un certificado general de salud.

Antes de usarlo, se comprueban configuración de bootloader, particiones, firma y política de seguridad del proyecto. No se invoca confirmación al principio de app_main solo para eliminar el estado pendiente. Tampoco se hace una actualización nueva mientras la actual siga sin resolver su prueba.

En el modelo, la aceptación y el mínimo cambian en una transición lógica. La implementación real verifica los retornos del SDK y su comportamiento ante cortes, incluidas las operaciones de seguridad que no equivalen a escribir un archivo común.

10 · Un modelo de aceptación y recuperación

Modelo ejecutable en Python

El registro empieza con A válida, build 1, nivel de seguridad 1 y datos en esquema 1. Instalar representa una descarga completa y comprobada al slot inactivo. El primer reinicio pone el candidato en prueba; otro reinicio sin confirmar intenta la reversión.

Firma, clave confiable, integridad y comprobación de arranque son resultados externos simulados. El estado nueva/pendiente/válida/inválida es una abstracción y no reproduce todos los estados ni la selección exacta de ESP-IDF. Los rechazos conservan el registro; una reversión procesada puede terminar en recuperación si no hay respaldo útil.

Python en el navegador. El programa usa la biblioteca estándar y funciona con Pyodide. Recorre éxito, reinicio sin aceptación, firma rechazada, mínimo de seguridad y migración destructiva, sin tocar hardware.

from copy import deepcopy


def nuevo():
    return dict(fase="estable", ejecutando="A", confirmada="A", seleccion=None,
                minimo=1, datos=1, diagnostico=False,
                slots=dict(A=dict(build=1, seguridad=1, esquema=1,
                                  modelo="placaA", estado="valida"), B=None))


def imagen(perfil):
    m = dict(build=2, seguridad=1, esquema=1, modelo="placaA", estado="nueva")
    if perfil == "seguridad2": m.update(build=3, seguridad=2)
    if perfil == "anterior": m.update(build=4, seguridad=1)
    if perfil == "otra_placa": m["modelo"] = "placaB"
    if perfil == "migracion": m["esquema"] = 2
    return m


def elegible(e, slot):
    m = e["slots"][slot] if slot else None
    return bool(m and m["estado"] == "valida" and
                m["seguridad"] >= e["minimo"] and m["esquema"] == e["datos"])


def aplicar(estado, accion, perfil="normal", firma=True, clave=True,
            integridad=True, autorizado=True, persistencia=True,
            salud=True, respaldo=True, arranque=True):
    e = deepcopy(estado)
    def fin(ok, motivo):
        return dict(estado=e, ok=ok, motivo=motivo)
    def volver(fallida):
        e["slots"][fallida]["estado"] = "invalida"
        e.update(seleccion=None, diagnostico=False)
        anterior = e["confirmada"]
        if not respaldo and anterior != fallida:
            e["slots"][anterior]["estado"] = "invalida"
        if anterior != fallida and elegible(e, anterior):
            e.update(ejecutando=anterior, fase="estable")
            return fin(True, "Reversión operativa a la versión confirmada compatible")
        e.update(ejecutando=None, fase="recuperacion")
        return fin(True, "Sin respaldo operativo elegible: requiere recuperación autorizada")
    if accion == "reiniciar":
        if e["fase"] == "prueba": return volver(e["ejecutando"])
        if e["fase"] == "preparada":
            destino = e["seleccion"]
            if not arranque: return volver(destino)
            e.update(ejecutando=destino, seleccion=None, fase="prueba", diagnostico=False)
            e["slots"][destino]["estado"] = "pendiente"
            return fin(True, "Primer arranque del candidato; todavía no está confirmado")
        return fin(True, "Reinicio sin cambio del estado de aceptación")
    if accion == "instalar":
        if e["fase"] != "estable": return fin(False, "La instalación requiere una aplicación estable")
        if not autorizado or not firma or not clave or not integridad:
            return fin(False, "Falla autorización, firma, clave confiable o integridad")
        m = imagen(perfil)
        if m["modelo"] != "placaA": return fin(False, "La firma no reemplaza la compatibilidad de hardware")
        if m["seguridad"] < e["minimo"]: return fin(False, "Nivel de seguridad inferior al mínimo aceptado")
        if not persistencia: return fin(False, "No se confirmó la escritura y selección durables")
        destino = "B" if e["ejecutando"] == "A" else "A"
        e["slots"][destino] = m
        e.update(seleccion=destino, fase="preparada", diagnostico=False)
        return fin(True, "Imagen comprobada en el destino inactivo; conserva la aplicación actual")
    if accion == "fallar":
        if e["fase"] != "prueba": return fin(False, "Solo se declara fallido un candidato en prueba")
        return volver(e["ejecutando"])
    if accion == "migrar":
        if e["fase"] != "prueba": return fin(False, "La migración del ejercicio requiere un candidato en prueba")
        esquema = e["slots"][e["ejecutando"]]["esquema"]
        if e["datos"] == esquema: return fin(False, "Los datos ya tienen el esquema requerido")
        if not persistencia: return fin(False, "No se confirmó la migración persistida")
        e.update(datos=esquema, diagnostico=False)
        return fin(True, "Migración destructiva simulada; puede impedir el funcionamiento del respaldo")
    if accion in ("diagnosticar", "confirmar"):
        if e["fase"] != "prueba": return fin(False, "La aceptación requiere un candidato en prueba")
        m = e["slots"][e["ejecutando"]]
        if not salud or m["esquema"] != e["datos"]:
            return fin(False, "Falla salud local o compatibilidad de los datos")
        if accion == "diagnosticar":
            e["diagnostico"] = True
            return fin(True, "Diagnóstico local del arranque actual superado")
        if not e["diagnostico"]: return fin(False, "Falta diagnóstico satisfactorio de este arranque")
        if not persistencia: return fin(False, "No se confirmó la aceptación durable")
        m["estado"] = "valida"
        e.update(confirmada=e["ejecutando"], fase="estable", diagnostico=False,
                 minimo=max(e["minimo"], m["seguridad"]))
        return fin(True, "Aplicación confirmada; mínimo de seguridad actualizado sin disminuir")
    return fin(False, "Acción desconocida")


estado = nuevo()
escenarios = 0


def paso(nombre, accion, esperado, **cfg):
    global estado, escenarios
    antes = deepcopy(estado)
    r = aplicar(estado, accion, **cfg)
    assert r["ok"] == esperado, nombre
    assert estado == antes, "El argumento no debe modificarse"
    if not esperado: assert r["estado"] == antes, "Rechazo sin cambios"
    estado = r["estado"]
    assert estado["minimo"] >= antes["minimo"]
    assert (estado["seleccion"] is not None) == (estado["fase"] == "preparada")
    if estado["fase"] == "estable": assert elegible(estado, estado["ejecutando"])
    if estado["fase"] == "prueba":
        assert estado["slots"][estado["ejecutando"]]["estado"] == "pendiente"
    if estado["fase"] == "recuperacion": assert estado["ejecutando"] is None
    escenarios += 1
    print(f'{nombre:30} {estado["fase"]:12} ejecuta={estado["ejecutando"]} mínimo={estado["minimo"]}')


paso("Firma rechazada", "instalar", False, firma=False)
paso("Clave ajena", "instalar", False, clave=False)
paso("Contenido alterado", "instalar", False, integridad=False)
paso("Sin autorización", "instalar", False, autorizado=False)
paso("Otra placa firmada", "instalar", False, perfil="otra_placa")
paso("Instalación no persistida", "instalar", False, persistencia=False)
paso("Preparar candidato", "instalar", True)
assert estado["ejecutando"] == "A"
paso("No aceptar antes de arrancar", "confirmar", False)
paso("Primer arranque", "reiniciar", True)
paso("No actualizar en prueba", "instalar", False)
paso("No aceptar sin diagnóstico", "confirmar", False)
paso("Diagnóstico fallido", "diagnosticar", False, salud=False)
paso("Reset sin confirmar", "reiniciar", True)
assert estado["ejecutando"] == "A" and estado["slots"]["B"]["estado"] == "invalida"
paso("Preparar otro intento", "instalar", True)
paso("Falla verificación al boot", "reiniciar", True, arranque=False)
assert estado["ejecutando"] == "A"
paso("Preparar nivel 2", "instalar", True, perfil="seguridad2")
paso("Arrancar nivel 2", "reiniciar", True)
assert estado["minimo"] == 1
paso("Diagnóstico satisfactorio", "diagnosticar", True)
paso("Aceptación no persistida", "confirmar", False, persistencia=False)
paso("Confirmar nivel 2", "confirmar", True)
assert estado["minimo"] == 2 and not elegible(estado, "A")
paso("Build mayor, seguridad menor", "instalar", False, perfil="anterior")
paso("Reset confirmado", "reiniciar", True)
assert estado["ejecutando"] == "B"

print("\nEscenario independiente: respaldo no disponible")
estado = nuevo()
paso("Preparar prueba", "instalar", True)
paso("Arrancar prueba", "reiniciar", True)
paso("Falla sin respaldo válido", "fallar", True, respaldo=False)
assert estado["fase"] == "recuperacion"
paso("No instalar desde recuperación", "instalar", False)

print("\nEscenario independiente: migración destructiva")
estado = nuevo()
paso("Preparar esquema 2", "instalar", True, perfil="migracion")
paso("Arrancar para migrar", "reiniciar", True)
paso("Datos aún incompatibles", "diagnosticar", False)
paso("Migración no persistida", "migrar", False, persistencia=False)
paso("Sobrescribir datos a 2", "migrar", True)
assert estado["datos"] == 2
paso("Falla después de migrar", "fallar", True)
assert estado["fase"] == "recuperacion" and estado["slots"]["A"]["estado"] == "valida"
print(f"\n{escenarios} escenarios superados; sin firmas ni eFuses reales.")

Los 32 escenarios comprueban resultados, rechazo sin cambios, mínimo no decreciente, selección coherente y estados de ejecución. Las aserciones adicionales distinguen reversión útil, respaldo bloqueado y datos incompatibles.

11 · Dos slots y decisiones de aceptación

Laboratorio de aceptación y respaldo

Aplicá una acción por vez. El perfil queda fijado al instalar; no cambia un candidato ya preparado. Cada flag representa una verificación externa o una condición del escenario. No ingreses claves ni certificados reales.

Aplicación confirmada y candidato

Fase de aceptación—
Aplicación operativa en el modelo—
Último slot confirmado—
Nivel mínimo de seguridad—
Datos persistidos—
Otro slot como respaldo operativo—

Escenario listo. A está confirmada; B está vacía.

Estado de los slots

Validez de imagen y compatibilidad de datos se evalúan por separado.
SlotBuildSeguridadEsquema requeridoEstado de imagenOperativaSeleccionada

Historial de decisiones

Últimas 30 acciones. Procesar una falla puede terminar en recuperación.
PasoAcciónResultadoFase finalMotivo

Registro simulado


        

Recorrido guiado

  1. Desactivá firma e intentá instalar: se rechaza y A sigue estable. Restaurala, probá la imagen de otra placa y observá que una firma correcta tampoco elimina esa incompatibilidad.
  2. Elegí el perfil normal e instalá. B queda nueva y seleccionada; A continúa operativa. Reiniciá: B pasa a pendiente y fase prueba.
  3. Intentá confirmar sin diagnosticar. Después desactivá salud e intentá diagnosticar. Ambos intentos se rechazan sin confirmar la aplicación.
  4. Reiniciá otra vez, todavía sin confirmar. B queda inválida y se recupera A. No hay un éxito implícito por haber iniciado B una vez.
  5. Con salud restaurada, elegí seguridad 2, instalá y reiniciá. El mínimo todavía vale 1: la aplicación anterior sigue disponible bajo esa política.
  6. Diagnosticá, desactivá persistencia e intentá confirmar: sigue en prueba. Restaurala y confirmá. El mínimo pasa a 2 y A deja de ser respaldo elegible por su nivel.
  7. Elegí build 4 con seguridad 1 e intentá instalar. El número de build es mayor, pero se rechaza por nivel inferior al mínimo.
  8. Creá un escenario nuevo. Restaurá las condiciones, instalá y arrancá un candidato normal. Desactivá respaldo y declaralo fallido: no queda alternativa y se entra en recuperación.
  9. Creá otro escenario nuevo, restaurá respaldo y elegí el perfil que requiere esquema 2. Instalá y arrancá. El diagnóstico no pasa mientras los datos sigan en 1.
  10. Simulá migración y después declaralo fallido. A conserva imagen válida, pero ya no puede trabajar con el esquema 2: también se requiere recuperación. La próxima práctica consiste en diseñar una migración que conserve una salida útil.

«Nuevo escenario ficticio» conserva las opciones de los controles y crea otro registro desde cero. Recargar la página también pierde esta simulación. No representa una forma de reducir el mínimo de un chip ni de borrar el estado de seguridad de una unidad.

¿Por qué aparece «procesada» si el resultado es recuperación?

El modelo pudo aplicar la decisión de descartar el candidato, pero no encontró respaldo operativo. No presenta ese resultado como éxito de actualización: la fase y el motivo indican que se requiere intervención autorizada.

12 · Probar el camino de salida

Pruebas y diagnóstico

  1. Definí confianza: qué claves autorizan qué producto y cómo se protegen.
  2. Comprobá artefactos negativos: firma incorrecta, clave ajena, contenido alterado y hardware incompatible.
  3. Verificá las particiones: capacidad final y preservación de una aplicación útil.
  4. Definí salud y plazo: criterios locales, estados seguros y dependencia de servicios externos.
  5. Ensayá aceptación fallida: diagnóstico negativo, retorno de error y reset sin confirmar.
  6. Ensayá ausencia de respaldo: imagen inválida, mínimo de seguridad y datos incompatibles.
  7. Probá migraciones: cortes, versiones salteadas y conservación o restauración del formato previo.
  8. Probá cambios de política: claves, nivel mínimo y compatibilidad del material de recuperación.
  9. Registrá evidencia: build, slot, estado, motivo y resultado de recuperación, sin secretos.
Un diagnóstico que distingue causas.
SíntomaQué revisar
Se rechaza una imagen firmadaClave admitida, cobertura de firma, formato, producto y política de versión
Vuelve a la anterior al reiniciarEstado pendiente, diagnóstico, confirmación y sus retornos
No existe reversión posibleOtra imagen válida y nivel mínimo aplicable
Arranca la anterior, pero no funcionaDatos migrados, configuración y dependencias que cambiaron
Un error tardío no revierteLa prueba inicial ya pudo estar confirmada; aplicar respuesta a fallos tardíos

Se cortan alimentación y red en puntos definidos del proceso real, no solo al final de una descarga. Un simulador lógico no demuestra atomicidad de flash, comportamiento de eFuses ni resistencia de la cadena de arranque. Esas garantías se validan con la plataforma, su configuración y hardware.

«Si está firmado, está libre de errores»

La firma autentica el contenido bajo una política. Las pruebas y el diagnóstico evalúan funcionamiento.

«Tengo dos slots, así que siempre puedo volver»

El otro slot puede no contener una imagen admisible o sus datos pueden haber dejado de ser compatibles.

«El hash incluido en el mismo archivo reemplaza la firma»

Se necesita un valor esperado autenticado y un mecanismo de autorización apropiado. Un atacante podría cambiar archivo y hash juntos.

«Confirmo al arrancar para que no me moleste el rollback»

Eso consume la oportunidad de prueba sin obtener evidencia de salud. Se confirma después del diagnóstico definido.

«Al volver al firmware anterior vuelven los datos anteriores»

Los datos persistidos no se restauran automáticamente al cambiar de slot. La migración necesita su propio plan.

«Subir el mínimo de seguridad no afecta la recuperación»

Puede excluir imágenes que antes eran admitidas. Se comprueba el respaldo y el procedimiento de recuperación antes de cambiar la política.

13 · Práctica guiada

Predecí antes de comprobar

1. Firma de una clave desconocida

La firma es matemáticamente correcta, pero la clave que la produjo no pertenece al conjunto autorizado. ¿Se acepta la imagen?

Ver solución

No. La verificación necesita una autoridad admitida para ese propósito. La corrección matemática no incorpora automáticamente una clave a la confianza del producto.

2. Reset en prueba

A está confirmada. B arrancó pendiente y se reinicia sin confirmar. Ambas tienen seguridad 1 y datos compatibles. ¿Qué intenta el modelo?

Ver solución

Descarta B y vuelve a A. El primer arranque no equivalía a aceptación.

3. Aceptación no persistida

El diagnóstico pasó, pero falla la persistencia al confirmar. ¿Se anuncia que la nueva aplicación quedó aceptada?

Ver solución

No. El modelo conserva el estado de prueba. La implementación comprueba el retorno y aplica su recuperación, sin inventar un éxito.

4. Build mayor y nivel menor

El mínimo vale 2 y llega build 4 con nivel de seguridad 1. ¿El número de build permite instalarlo?

Ver solución

No. Build y nivel de seguridad cumplen funciones distintas. El candidato queda por debajo del mínimo.

5. Momento de elevar el mínimo

B tiene seguridad 2 y está en prueba; A tiene 1. ¿Por qué el modelo mantiene mínimo 1 hasta confirmar B?

Ver solución

Para conservar la posibilidad de recuperar A mientras se evalúa B. Al confirmar y elevar el mínimo, A deja de ser elegible por esa política.

6. Migración destructiva

B sobrescribe los únicos datos de esquema 1 a 2 y luego falla. A solo entiende 1. ¿Una firma válida de A garantiza recuperación operativa?

Ver solución

No. Puede conservar autenticidad y aun así no leer los datos. Se necesitaba compatibilidad, una copia recuperable u otra estrategia de migración.

7. Caída del servicio externo

La red ya estaba caída y el firmware nuevo no puede contactar la nube. ¿Siempre debe considerarse un defecto que exige reversión?

Ver solución

No automáticamente. El producto distingue salud local, requisitos esenciales y disponibilidad externa. Se aplica el criterio diseñado con su plazo y evidencia.

8. Fallo un mes después

La aplicación fue confirmada al instalarse y falla un mes después. ¿Debe asumirse que el rollback inicial actuará por sí solo?

Ver solución

No. La aceptación inicial ya terminó. Hace falta la respuesta a fallos tardíos: gestión autorizada, otra actualización o recuperación según el producto.

14 · Síntesis

Ideas para recordar

  • Firma, compatibilidad y salud operativa aportan evidencias diferentes.
  • La clave privada de publicación se protege fuera de la flota; la confianza del verificador tiene un origen definido.
  • Los slots conservan imágenes y los metadatos controlan su selección y aceptación.
  • El candidato arranca en prueba y requiere diagnóstico antes de confirmar.
  • Un reset sin aceptación no debe convertirse en éxito implícito.
  • La reversión necesita otra imagen admisible y capaz de operar con los datos existentes.
  • Anti-rollback puede excluir un respaldo anterior aunque siga correctamente firmado.
  • Una migración de datos puede cerrar la salida que ofrecía el firmware anterior.
  • El modelo lógico no sustituye pruebas de cortes y recuperación en la plataforma real.

¿Listo para el tema 45?

Antes de avanzar, definí los criterios de aceptación de tu firmware y explicá qué aplicación y qué datos quedarán disponibles si el candidato falla. Anotá también qué evidencia reportará el equipo para distinguir actualización aceptada, reversión y recuperación pendiente.

En el próximo tema, Gestión y monitoreo de flotas, organizamos versiones, estado, registros y diagnóstico para muchos dispositivos.