Google is committed to advancing racial equity for Black communities. See how.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Implementando dm-verity

Android 4.4 y superior admite el arranque verificado a través de la función de kernel opcional device-mapper-verity (dm-verity), que proporciona una verificación transparente de la integridad de los dispositivos de bloque. dm-verity ayuda a prevenir rootkits persistentes que pueden retener privilegios de root y comprometer dispositivos. Esta función ayuda a los usuarios de Android a asegurarse de que, al iniciar un dispositivo, esté en el mismo estado que cuando se utilizó por última vez.

Las aplicaciones potencialmente dañinas (PHA) con privilegios de root pueden esconderse de los programas de detección y enmascararse. El software de enraizamiento puede hacer esto porque a menudo tiene más privilegios que los detectores, lo que permite que el software "mienta" a los programas de detección.

La función dm-verity le permite observar un dispositivo de bloque, la capa de almacenamiento subyacente del sistema de archivos, y determinar si coincide con su configuración esperada. Lo hace utilizando un árbol hash criptográfico. Para cada bloque (normalmente 4k), hay un hash SHA256.

Debido a que los valores hash se almacenan en un árbol de páginas, solo se debe confiar en el hash "raíz" de nivel superior para verificar el resto del árbol. La capacidad de modificar cualquiera de los bloques equivaldría a romper el hash criptográfico. Consulte el diagrama siguiente para ver una descripción de esta estructura.

dm-verity-hash-table

Figura 1. Tabla hash dm-verity

Se incluye una clave pública en la partición de arranque, que debe ser verificada externamente por el fabricante del dispositivo. Esa clave se usa para verificar la firma de ese hash y confirmar que la partición del sistema del dispositivo está protegida y sin cambios.

Operación

La protección dm-verity vive en el kernel. Entonces, si el software de enraizamiento compromete el sistema antes de que aparezca el kernel, retendrá ese acceso. Para mitigar este riesgo, la mayoría de los fabricantes verifican el kernel utilizando una clave grabada en el dispositivo. Esa clave no se puede cambiar una vez que el dispositivo sale de fábrica.

Los fabricantes usan esa clave para verificar la firma en el cargador de arranque de primer nivel, que a su vez verifica la firma en los niveles posteriores, el cargador de arranque de la aplicación y, finalmente, el kernel. Cada fabricante que desee aprovechar el arranque verificado debe tener un método para verificar la integridad del kernel. Suponiendo que el núcleo ha sido verificado, el núcleo puede mirar un dispositivo de bloque y verificarlo mientras está montado.

Una forma de verificar un dispositivo de bloque es hash directamente en su contenido y compararlo con un valor almacenado. Sin embargo, intentar verificar un dispositivo de bloque completo puede llevar un período prolongado y consumir gran parte de la energía de un dispositivo. Los dispositivos tardarían mucho en arrancar y luego se agotarían significativamente antes de su uso.

En cambio, dm-verity verifica los bloques individualmente y solo cuando se accede a cada uno. Cuando se lee en la memoria, el bloque se codifica en paralelo. Luego, el hash se verifica en el árbol. Y dado que leer el bloque es una operación tan cara, la latencia introducida por esta verificación a nivel de bloque es comparativamente nominal.

Si la verificación falla, el dispositivo genera un error de E / S que indica que el bloque no se puede leer. Aparecerá como si el sistema de archivos se hubiera dañado, como se esperaba.

Las aplicaciones pueden optar por continuar sin los datos resultantes, como cuando esos resultados no son necesarios para la función principal de la aplicación. Sin embargo, si la aplicación no puede continuar sin los datos, fallará.

Corrección de errores hacia adelante

Android 7.0 y superior mejora la robustez de dm-verity con corrección de errores hacia adelante (FEC). La implementación de AOSP comienza con el código común de corrección de errores de Reed-Solomon y aplica una técnica llamada entrelazado para reducir la sobrecarga de espacio y aumentar la cantidad de bloques corruptos que se pueden recuperar. Para obtener más detalles sobre FEC, consulte Arranque verificado estrictamente aplicado con corrección de errores .

Implementación

Resumen

  1. Genere una imagen del sistema ext4.
  2. Genere un árbol hash para esa imagen.
  3. Construya una tabla dm-verity para ese árbol hash.
  4. Firme esa tabla dm-verity para producir una firma de tabla.
  5. Agrupe la firma de la tabla y la tabla dm-verity en metadatos de verdad.
  6. Concatenar la imagen del sistema, los metadatos de veracidad y el árbol hash.

Consulte The Chromium Projects - Verified Boot para obtener una descripción detallada del árbol hash y la tabla dm-verity.

Generando el árbol de hachís

Como se describe en la introducción, el árbol hash es parte integral de dm-verity. La herramienta cryptsetup generará un árbol hash para usted. Alternativamente, aquí se define uno compatible:

<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>

Para formar el hash, la imagen del sistema se divide en la capa 0 en bloques de 4k, a cada uno se le asigna un hash SHA256. La capa 1 se forma uniendo solo esos hashes SHA256 en bloques de 4k, lo que da como resultado una imagen mucho más pequeña. La capa 2 se forma de manera idéntica, con los hash SHA256 de la capa 1.

Esto se hace hasta que los hashes SHA256 de la capa anterior puedan caber en un solo bloque. Cuando obtiene el SHA256 de ese bloque, tiene el hash raíz del árbol.

El tamaño del árbol hash (y el uso de espacio en disco correspondiente) varía con el tamaño de la partición verificada. En la práctica, el tamaño de los árboles de hachís tiende a ser pequeño, a menudo menos de 30 MB.

Si tiene un bloque en una capa que no está completamente lleno de forma natural por los hash de la capa anterior, debe rellenarlo con ceros para lograr los 4k esperados. Esto le permite saber que el árbol hash no se ha eliminado y, en cambio, se completa con datos en blanco.

Para generar el árbol de hash, concatenar los hash de la capa 2 con los de la capa 1, los de la capa 3 con los de la capa 2, y así sucesivamente. Escriba todo esto en el disco. Tenga en cuenta que esto no hace referencia a la capa 0 del hash raíz.

En resumen, el algoritmo general para construir el árbol hash es el siguiente:

  1. Elija una sal aleatoria (codificación hexadecimal).
  2. Divida la imagen de su sistema en bloques de 4k.
  3. Para cada bloque, obtenga su hash SHA256 (salado).
  4. Concatenar estos hashes para formar un nivel
  5. Rellene el nivel con ceros hasta un límite de bloque de 4k.
  6. Concatenar el nivel a su árbol hash.
  7. Repita los pasos 2 a 6 utilizando el nivel anterior como fuente para el siguiente hasta que tenga un solo hash.

El resultado de esto es un solo hash, que es su hash raíz. Esto y su sal se utilizan durante la construcción de su tabla de mapeo dm-verity.

Construyendo la tabla de mapeo dm-verity

Construya la tabla de mapeo dm-verity, que identifica el dispositivo de bloque (o destino) para el kernel y la ubicación del árbol hash (que es el mismo valor). Este mapeo se usa para la generación y arranque de fstab . La tabla también identifica el tamaño de los bloques y el hash_start, la ubicación de inicio del árbol hash (específicamente, su número de bloque desde el comienzo de la imagen).

Consulte cryptsetup para obtener una descripción detallada de los campos de la tabla de asignación de destino de verdad.

Firmando la tabla dm-verity

Firme la tabla dm-verity para producir una firma de tabla. Al verificar una partición, primero se valida la firma de la tabla. Esto se hace con una clave en su imagen de arranque en una ubicación fija. Las claves se incluyen normalmente en los sistemas de construcción de los fabricantes para su inclusión automática en dispositivos en una ubicación fija.

Para verificar la partición con esta combinación de firma y clave:

  1. Agregue una clave RSA-2048 en formato compatible con libmincrypt a la partición /boot en /verity_key . Identifique la ubicación de la clave utilizada para verificar el árbol hash.
  2. En el fstab para la entrada relevante, agregue verify a los indicadores fs_mgr .

Agrupar la firma de la tabla en metadatos

Agrupe la firma de la tabla y la tabla dm-verity en metadatos de verdad. Todo el bloque de metadatos está versionado para que se pueda extender, como para agregar un segundo tipo de firma o cambiar algún orden.

Como prueba de cordura, se asocia un número mágico con cada conjunto de metadatos de la tabla que ayuda a identificar la tabla. Dado que la longitud se incluye en el encabezado de la imagen del sistema ext4, esto proporciona una forma de buscar los metadatos sin conocer el contenido de los datos en sí.

Esto asegura que no haya elegido verificar una partición no verificada. Si es así, la ausencia de este número mágico detendrá el proceso de verificación. Este número se parece a:
0xb001b001

Los valores de bytes en hexadecimal son:

  • primer byte = b0
  • segundo byte = 01
  • tercer byte = b0
  • cuarto byte = 01

El siguiente diagrama muestra el desglose de los metadatos de verdad:

<magic number>|<version>|<signature>|<table length>|<table>|<padding>
\-------------------------------------------------------------------/
\----------------------------------------------------------/   |
                            |                                  |
                            |                                 32K
                       block content

Y esta tabla describe esos campos de metadatos.

Tabla 1. Campos de metadatos de Verity

Campo Propósito Talla Valor
número mágico utilizado por fs_mgr como comprobación de cordura 4 bytes 0xb001b001
versión utilizado para versionar el bloque de metadatos 4 bytes actualmente 0
firma la firma de la tabla en forma rellenada PKCS1.5 256 bytes
longitud de la mesa la longitud de la tabla dm-verity en bytes 4 bytes
mesa la tabla dm-verity descrita anteriormente bytes de longitud de tabla
relleno esta estructura tiene un relleno de 0 a 32k de longitud 0

Optimización de dm-verity

Para obtener el mejor rendimiento de dm-verity, debe:

  • En el kernel, active NEON SHA-2 para ARMv7 y las extensiones SHA-2 para ARMv8.
  • Experimente con diferentes configuraciones de lectura anticipada y prefetch_cluster para encontrar la mejor configuración para su dispositivo.