- dm-verity verifica bloques en tiempo real mediante un árbol de hashes criptográficos, impidiendo cambios silenciosos en particiones críticas.
- La confianza se ancla en el hash raíz y su firma, integrados en una cadena de arranque verificado junto a bootloaders, kernel y Secure Boot.
- Android, Linux y sistemas embebidos usan dm-verity para raíces de solo lectura, combinándolo con FEC, TPM y cifrado para endurecer el sistema.
- La actualización de sistemas con dm-verity se basa en imágenes inmutables, esquemas A/B y overlays, evitando cambios directos sobre la raíz verificada.

Si te mueves en el mundo de Android, Linux o sistemas embebidos, es muy probable que hayas visto mensajes como “dm-verity corruption” o que te suene eso de arranque verificado, AVB o Secure Boot. Detrás de todo ese tinglado está una pieza clave del kernel: dm-verity, un mecanismo pensado para asegurar que el sistema de archivos no ha sido manipulado, ni mientras el dispositivo está encendido ni entre reinicios.
Puede parecer algo muy bajo nivel, pero en realidad afecta a cosas tan cotidianas como que tu móvil no mantenga un malware escondido en la partición de sistema, o que un router o un servidor “cerrado” arranque siempre en el mismo estado confiable. dm-verity se apoya en un árbol de hashes SHA-256 y en firmas criptográficas para comprobar, bloque a bloque, que los datos del disco siguen siendo los que deberían, y si algo no cuadra, el kernel puede devolver errores de lectura, reiniciar el equipo o incluso entrar en pánico para no seguir ejecutando código dudoso.
Qué es dm-verity y qué problema resuelve
dm-verity es un destino (target) del subsistema device-mapper del kernel Linux que permite verificar en tiempo real la integridad de un dispositivo de bloques, normalmente una partición o imagen que contiene el sistema de archivos raíz o una partición crítica (por ejemplo, /system en Android). En lugar de leer el disco “a ciegas”, cada bloque que se accede se comprueba criptográficamente contra un árbol de hashes precomputado.
En Android desde la versión 4.4 y en muchas distribuciones Linux modernas, dm-verity es la base del arranque verificado: se asegura de que la partición del sistema esté exactamente igual que cuando se generó la imagen. Esto dificulta que un rootkit u otro tipo de malware consiga permanecer tras un reinicio inyectando ficheros o binarios maliciosos en el sistema.
Una de las gracias de este enfoque es que los dispositivos dm-verity se presentan como dispositivos de bloques normales bajo /dev/mapper, lo que permite montarlos como si fuesen un disco más. Para el sistema de ficheros de encima (ext4, EROFS, squashfs, etc.) todo parece estándar, pero cada lectura pasa por el filtro criptográfico de verity.
Por qué es necesario: malware con root y arranque confiable

En sistemas como Android, las aplicaciones o binarios que consiguen privilegios de root pueden esconderse mejor que los propios sistemas de detección. Al tener más permisos que el antivirus o que las herramientas de seguridad, pueden “mentir” sobre ficheros, procesos o configuraciones, dificultando su detección y borrado.
Sin un mecanismo como dm-verity, un atacante puede modificar binarios del sistema, librerías o scripts de arranque para conseguir persistencia, y así evitar que herramientas que permiten verificar la integridad de archivos detecten la alteración; es decir, seguir ahí aunque apagues y vuelvas a encender el dispositivo. Esa es la pesadilla clásica de los rootkits persistentes.
dm-verity actúa como un guardián en la base del sistema: el kernel solo acepta como válidos los bloques que cumplan con el hash esperado del árbol de verificación. Si alguien ha tocado “a pelo” la partición de sistema, los hashes dejarán de coincidir y el kernel lo detectará en cuanto intente leer los datos alterados.
Cómo funciona dm-verity por dentro
La idea básica es sencilla pero potente: se construye un árbol criptográfico de hashes (normalmente SHA-256) sobre todos los bloques del dispositivo, de forma jerárquica. Ese árbol se almacena en disco y, durante el uso normal, se utiliza para validar cada bloque leído.
Estructura del árbol de hashes
El árbol de verificación se forma por niveles. En la capa 0 están los datos reales (por ejemplo, la imagen ext4/EROFS del sistema), divididos en bloques de 4K. De cada uno de esos bloques se calcula un hash SHA-256 (normalmente con una sal aleatoria para endurecer ataques de precomputación).
Los hashes de la capa 0 se concatenan para formar la capa 1. La capa 1 se vuelve a agrupar en bloques de 4K y se calcula un SHA-256 de cada bloque resultante, produciendo la capa 2. El proceso se repite capa a capa hasta que todo el conjunto de hashes cabe en un único bloque; el hash de ese bloque final es el hash raíz, que representa todo el árbol.
Cuando una capa no llena un bloque completo, se rellena con ceros hasta llegar a los 4K. Esto evita ambigüedades y permite detectar intentos de “recortar” el árbol reemplazando partes por datos arbitrarios: la estructura esperada incluye ese padding conocido de ceros.
En disco, el árbol se almacena concatenando niveles desde la capa superior hacia abajo (excluyendo la capa 0 de datos). El tamaño total del árbol varía según el tamaño de la partición verificada, pero en la práctica suele ser bastante pequeño, típicamente menos de 30 MB incluso para particiones de sistema grandes.
Versiones de formato y algoritmos de hash
El formato en el que se guardan los bloques de hash ha ido evolucionando. La versión 0 del formato, usada originalmente por Chromium OS, añadía la sal al final al calcular el hash y almacenaba los digests de forma continua, rellenando con ceros el resto del bloque.
La versión 1, recomendada para sistemas nuevos, antepone la sal al dato al calcular el hash y rellena cada digest hasta una potencia de dos. Esto mejora la alineación y la robustez frente a algunos tipos de ataques o corrupciones. En la tabla dm-verity se indica también el algoritmo usado (sha1, sha256, etc.), aunque hoy en día lo razonable es emplear SHA-256.
Cálculo del hash raíz paso a paso
Si se quiere construir el árbol a mano, el esquema general es claro. Primero se elige una sal aleatoria en formato hexadecimal, se divide la imagen en bloques de 4K y, para cada bloque, se calcula su SHA-256 salado. Esos hashes forman el primer nivel “lógico” encima de los datos.
Luego, se concatenan los hashes hasta llenar bloques de 4K; si falta espacio, se rellena con ceros. Cada bloque resultante también se hashea con SHA-256 para formar el siguiente nivel del árbol. Este proceso de “hash sobre hashes” se repite hasta quedarnos con un único hash: el hash raíz.
En implementaciones prácticas, herramientas como cryptsetup/veritysetup se encargan de todo este cálculo y generan directamente el archivo de árbol (verity.bin) y el valor de hash raíz (roothash), listos para usarse en la tabla dm-verity o en los metadatos firmados.
La tabla dm-verity: describiendo qué y cómo se verifica
Para que el kernel pueda usar dm-verity, necesita una descripción precisa de dónde están los datos, dónde está el árbol de hashes y qué parámetros usar. Esa descripción es la tabla dm-verity, una línea de parámetros que el device-mapper interpreta al crear el dispositivo lógico verificado.
En una versión simplificada típica, la definición puede verse como:
<nombre mapeo> <dispositivo datos> <tamaño bloque datos> <tamaño bloque hash> <tamaño imagen en bloques> <hash_start> <root hash> <salt>
Los campos más importantes de la tabla dm-verity suelen ser:
- dev: dispositivo que contiene los datos a verificar (por ejemplo, una partición /dev/sdXN o un par mayor:menor).
- hash_dev: dispositivo que almacena el árbol de hashes; puede ser el mismo que dev, siempre que hash_start apunte fuera del rango de datos verificados.
- data_block_size: tamaño de bloque de datos en bytes, normalmente 4096.
- hash_block_size: tamaño de bloque para los hashes, también suele ser 4096.
- num_data_blocks: número de bloques de datos que se van a proteger.
- hash_start_block: offset en bloques desde el inicio del dispositivo hasta donde empieza el árbol de hash.
- algorithm: algoritmo de hash (sha256 es el estándar de facto).
- digest (root hash): hash del bloque raíz del árbol, expresado en hexadecimal; es el “ancla” de confianza.
- salt: sal utilizada al calcular los hashes, también en hexadecimal.
Además de estos campos básicos, existen parámetros opcionales que ajustan cómo reacciona el sistema ante corrupción o errores. Por ejemplo, se puede indicar que ante corrupción se reinicie el sistema, se haga panic, se ignore registrando sólo en logs, o que se active la recuperación FEC antes de fallar.
Opciones avanzadas de la tabla: corrupción, FEC y rendimiento
dm-verity incluye un conjunto de flags para perfilar el comportamiento. ignore_corruption permite seguir leyendo aunque se detecte corrupción, pero dejando rastro en los logs, útil en entornos donde se prioriza disponibilidad frente a integridad estricta.
Si se busca mano dura, restart_on_corruption o panic_on_corruption fuerzan un reinicio o un panic cuando un bloque no pasa la verificación. Existen variantes similares para errores de E/S (restart_on_error, panic_on_error). También hay una opción ignore_zero_blocks que evita verificar bloques esperados a ceros y devuelve ceros directamente.
Para sistemas que integran corrección hacia delante, use_fec_from_device junto con fec_roots, fec_blocks y fec_start activan el uso de códigos Reed-Solomon. Con FEC, ante un fallo de verificación se puede intentar reconstruir el bloque usando la información redundante antes de darlo por perdido.
Otras opciones, como check_at_most_once, permiten verificar cada bloque solo la primera vez que se accede, reduciendo overhead a costa de no detectar modificaciones en caliente; es un compromiso entre seguridad y rendimiento. Flags como root_hash_sig_key_desc permiten que el kernel valide una firma PKCS7 del root hash usando claves almacenadas en el keyring.
Firma, metadatos y número mágico de verity
Para que todo tenga sentido, el valor del root hash debe ser confiable. En Android clásico se incluye una clave pública en la partición de arranque, y el fabricante se encarga de verificarla por fuera. Esa clave se usa para validar la firma del root hash o de la tabla dm-verity, asegurando que el árbol de hash no ha sido manipulado.
Los metadatos de verity encapsulan esa información. En un bloque de 32K se agrupan un número mágico, versión, firma, longitud y contenido de la tabla, más relleno con ceros. Esa estructura controlada permite localizar los metadatos y validarlos sin ambigüedad.
Los campos típicos de estos metadatos incluyen:
- Número mágico: valor fijo 0xb001b001, usado por componentes como fs_mgr para reconocer que se trata de un bloque verity válido.
- Versión: actualmente 0, sirve para introducir cambios de formato en el futuro.
- Firma: firma de la tabla dm-verity, normalmente PKCS1.5 con una clave RSA-2048 (256 bytes).
- Longitud de la tabla: tamaño en bytes de la tabla dm-verity almacenada a continuación.
- Tabla: la propia tabla dm-verity serializada.
- Relleno: ceros hasta completar los 32.000 bytes de bloque.
Si al analizar el final de la imagen de sistema no se encuentra ese número mágico, se asume que la partición no está preparada para verity y el proceso de verificación no se activa. Así se evita, por ejemplo, tratar como verificada una partición que no lo está.
En Android, fs_mgr y el fichero fstab controlan qué particiones se verifican. Basta con añadir la marca de verificación (por ejemplo, “verify” en las flags fs_mgr) y colocar la clave pública adecuada en /boot/verity_key para que el flujo de verificación end-to-end entre en acción.
Cómo se encadena con el arranque verificado
De poco serviría dm-verity si el atacante pudiera colar un kernel o un bootloader modificado que acepte cualquier cosa. Por eso, en dispositivos móviles y plataformas seguras, el hash raíz y la tabla dm-verity forman parte de una cadena de confianza que comienza en hardware.
Lo habitual es que el fabricante queme una clave pública en el dispositivo. Esa clave valida la firma del primer bootloader, que a su vez comprueba el siguiente nivel, el bootloader de apps y, finalmente, la imagen de kernel. A partir de ahí, el kernel, ya verificado, asume el control y utiliza dm-verity para extender esa confianza hasta la partición de sistema.
En Android modernos con AVB (Android Verified Boot 2.0), el bootloader incorpora libavb y lee descriptores de hashtree en las particiones o en vbmeta. Con esa información construye los parámetros dm-verity y los pasa al kernel en la línea de comandos, junto con indicaciones como si hay FEC, qué hacer en caso de corrupción, etc.
dm-verity en Android: system-as-root, AVB y mensajes de corrupción
Android lleva años apoyándose en dm-verity. Desde Android 4.4 se usa como base del arranque verificado, y a partir de Android 10 el diseño system-as-root integra directamente el rootfs en system.img, eliminando muchos montajes clásicos y obligando a manejar dm-verity desde un init de primera etapa.
Con system-as-root y las OTA modernas, la partición de sistema suele ser de solo lectura, protegida mediante un hashtree verificado. El kernel la ve a través de un dispositivo dm-verity, de forma transparente para la capa superior de Android.
Slots A/B, vbmeta y errores “dm-verity corruption”
En dispositivos con esquema A/B, es bastante fácil que algo se desajuste. Si flasheas boot o vbmeta sin que casen con el roothash y el árbol real de la partición de sistema, el resultado típico es el temido mensaje “dm-verity corruption, your device is not trusted”.
Para saltarse la verificación hay comandos como fastboot flash –disable-verity –disable-verification vbmeta vbmeta.img, o en algunos fabricantes, fastboot oem disable_dm_verity. Pero, ojo: eso desactiva el arranque verificado y deja de haber garantías de integridad, aunque consigas arrancar sin mensajes molestos.
La forma “limpia” de arreglarlo pasa por asegurarte de que las imágenes de system, boot y vbmeta sean coherentes entre sí, regenerar (o restaurar) el árbol de verity y actualizar las firmas o descriptores para que el roothash esperado coincida con el real. Solo así mantienes la cadena de confianza sin trucos peligrosos.
Relación con TWRP, bootloader desbloqueado y mods
En el mundo real, mucha gente se topa con dm-verity al instalar ROMs, kernels modificados o hacer root. Para jugar con TWRP, flashear firmwares o instalar mods, suele ser necesario tener el bootloader desbloqueado, porque el arranque verificado impide arrancar imágenes no firmadas por el fabricante.
Hay procedimientos que recomiendan, por ejemplo, flashear primero un firmware concreto, reiniciar en bootloader y ejecutar comandos como “fastboot oem disable_dm_verity” seguido de “fastboot oem enable_dm_verity”, y luego instalar otro firmware más reciente. Estas maniobras buscan “resetear” el estado de verity y que las nuevas imágenes se acepten sin mensajes de corrupción.
Si después de golpes o errores de flasheo empiezas a ver avisos de “dm-verity corruption” en cada reinicio, lo prudente es revisar que el sistema de particiones no tenga daños físicos y que las imágenes que estás usando sean las correctas para tu modelo. A veces un simple desajuste entre firmware de modem, boot y system puede liar el arranque verificado.
dm-verity en Linux de escritorio y servidores (systemd, veritysetup)
dm-verity no es exclusivo de Android. En distribuciones Linux modernas, especialmente con systemd, se está popularizando como base para sistemas raíz de solo lectura y alta confianza, muy similar a cómo funcionan algunos routers, appliances o cajas multimedia.
Un montaje típico de raíz con dm-verity incluye: una imagen o partición raíz, un fichero con el árbol de verity (verity.bin), el valor de root hash, las unidades de systemd-veritysetup y los parámetros apropiados en la línea de kernel. Opcionalmente se añade un UKI (Unified Kernel Image) firmado y Secure Boot para que el conjunto quede bien blindado.
Esquema de particionado y sistema de ficheros
La recomendación habitual es reservar una partición específica para los hashes. Una disposición muy usada consiste en: partición EFI (ESP), partición XBOOTLDR para los UKI, partición raíz (con o sin cifrado), partición VERITY para el árbol y, opcionalmente, particiones /home y /var escriturables.
En vez de ext4 clásico para la raíz, EROFS es una opción muy interesante: es de solo lectura por diseño, tiene muy buen rendimiento en flash y SSD y soporta compresión lz4 de fábrica. No es casualidad que se use ampliamente en móviles Android en combinación con dm-verity.
Archivos que necesitan escritura y trucos habituales
Si la raíz va montada como solo lectura, hay que pensar bien qué ficheros necesitan poder cambiarse. Muchos programas esperan escribir en /etc, /var o rutas similares. En lugar de hacer /etc completamente escribible, es más fino mover solo lo necesario a /var/etc y enlazarlo simbólicamente desde /etc.
Por ejemplo, las conexiones de NetworkManager se pueden mover a /var/etc/NetworkManager/system-connections y dejar un symlink desde /etc/NetworkManager/system-connections. Así no se rompe el diseño de raíz inmutable, pero las configuraciones que deben cambiar siguen pudiendo hacerlo.
Para descubrir qué se escribe realmente durante el arranque y la ejecución, se puede usar dracut-overlayroot, que monta un overlay tmpfs sobre la raíz y deja cualquier escritura real registrada en /run/overlayroot/u. Después de usar el sistema un rato, basta con inspeccionar ese directorio para saber qué hay que sacar fuera de la raíz verificada.
En Arch Linux es habitual también reubicar la base de datos de pacman a /usr/lib/pacman y la caché a /var/lib/pacman, de forma que la imagen raíz siempre refleje el estado del sistema “sellado”, mientras las operaciones de sincronización y actualización se hagan en zonas escribibles.
Creación del verity y configuración de arranque con systemd
El flujo típico en un Linux que quiere usar dm-verity para la raíz sería:
- Arrancar desde un medio live y montar la raíz como solo lectura, una vez que se ha dejado el sistema exactamente como se desea “congelarlo”.
- Ejecutar veritysetup format root-device verity-device para generar el árbol de hashes y el root hash. El comando suele imprimir la línea con el Root Hash, que se guarda en un fichero (por ejemplo, roothash.txt).
- Probar el mapeo con veritysetup open, creando un /dev/mapper/root verificado y montándolo para comprobar que todo funciona.
Luego toca ajustar la línea de comandos del kernel. Con systemd se usan parámetros como systemd.verity=1, roothash=…, systemd.verity_root_data=… y systemd.verity_root_hash=…, además de opciones como systemd.verity_root_options=restart-on-corruption o panic-on-corruption según la dureza deseada.
Si se emplea un UKI, todos estos parámetros van integrados en la imagen kernel.efi, que se firma y se arranca con Secure Boot. Esto evita que alguien cambie el roothash en la línea de comandos sin invalidar la firma, manteniendo el modelo de confianza.
Secure Boot, cifrado y TPM: encajando las piezas
dm-verity solo garantiza integridad, no confidencialidad. Los datos pueden verse si no están cifrados, pero no pueden alterarse sin ser detectados. Por eso, a menudo se combina con cifrado (LUKS) y con un TPM que proteja las claves.
Una estrategia muy usada es anclar las claves de descifrado LUKS a ciertos PCRs del TPM con systemd-cryptenroll (PCR 0, 1, 5, 7, por ejemplo), de forma que cambiar firmware, layout de particiones o estado de Secure Boot invalide las claves. Esto evita que un atacante desactive Secure Boot para colar un kernel que ignore verity sin romper a la vez la cadena de descifrado.
Si se usa systemd-boot, el bootloader mide la imagen kernel.efi en el PCR 4. Si esa medida cambia, las claves asociadas no se liberan y la partición cifrada no se abre. Es otro eslabón más para asegurar que kernel, initramfs y cmdline (incluyendo roothash) no han sido trucados.
Uso más allá de la raíz: otras particiones, overlays y actualizaciones
Aunque lo más habitual es proteger la raíz, dm-verity puede aplicarse a otras particiones que se montan en el arranque. En sistemas con systemd, estas particiones adicionales se describen en /etc/veritytab y las configura systemd-veritysetup@.service automáticamente.
Eso sí, una partición no raíz verificada es menos fuerte desde el punto de vista de seguridad: se puede remontar en lectura/escritura con relativa facilidad, y un usuario con root puede llegar a desactivar verity sobre ella. Aun así, es útil para datos que quieras monitorear o para imágenes de solo lectura montadas en otros puntos.
En cuanto a actualizaciones, un root de solo lectura verificado cambia el modelo mental. No se espera que el administrador haga un “pacman -Syu” o similar sobre la raíz en producción, sino que se generen imágenes nuevas del sistema, con sus árboles verity correspondientes, y se desplieguen de forma transaccional.
Hay varias estrategias para esto: usar herramientas como systemd-sysupdate y systemd-repart para descargar y flashear nuevas imágenes, o plantear un esquema A/B con dos raíces y dos verity, donde actualizas la partición inactiva y luego intercambias roles.
Si se quiere algo más flexible, se puede montar la raíz verificada como lower dir en un OverlayFS, con una capa superior en tmpfs o disco. Así, los cambios se aplican en la capa superior, pero la base sigue siendo la imagen verificada. Incluso se puede optar por persistencia opcional o efímera (por ejemplo, systemd.volatile=overlay) para tener “sesiones desechables”.
En el mundo de escritorio, tecnologías como Flatpak encajan bien con esta filosofía, ya que instalan y actualizan aplicaciones en /var y /home, sin tocar la raíz protegida por verity. De esta manera se mantiene un sistema base inmutable y se gestionan apps de forma independiente.
Todo este ecosistema hace que dm-verity sea mucho más que una curiosidad del kernel: es la piedra angular de sistemas inmutables, móviles y embebidos que necesitan arrancar siempre en un estado conocido, detectando cualquier manipulación del almacenamiento, y se integra con arranque verificado, Secure Boot, cifrado y TPM para ofrecer un modelo de seguridad moderno sin sacrificar demasiado rendimiento ni flexibilidad.
Redactor apasionado del mundo de los bytes y la tecnología en general. Me encanta compartir mis conocimientos a través de la escritura, y eso es lo que haré en este blog, mostrarte todo lo más interesante sobre gadgets, software, hardware, tendencias tecnológicas, y más. Mi objetivo es ayudarte a navegar por el mundo digital de forma sencilla y entretenida.