A partir de Android 12, el módulo de Android Runtime (ART) es un módulo de línea principal. Es posible que la actualización del módulo requiera que se vuelvan a compilar los artefactos de compilación por adelantado (AOT) de los archivos JAR de la ruta de acceso de inicio y el servidor del sistema. Debido a que estos artefactos son sensibles a la seguridad, Android 12 emplea una función llamada firma en el dispositivo para evitar que se manipulen. En esta página, se describe la arquitectura de firma en el dispositivo y sus interacciones con otras funciones de seguridad de Android.
Diseño de alto nivel
La firma integrada en el dispositivo tiene dos componentes principales:
odrefresh
es parte del módulo de la línea principal de ART. Es responsable de generar los artefactos del entorno de ejecución. Verifica los artefactos existentes en función de la versión instalada del módulo ART, los archivos JAR de bootclasspath y los archivos JAR del servidor del sistema para determinar si están actualizados o si deben volver a generarse. Si se deben volver a generar,odrefresh
los genera y almacena.odsign
es un objeto binario que forma parte de la plataforma de Android. Se ejecuta durante el inicio anticipado, justo después de que se activa la partición/data
. Su responsabilidad principal es invocarodrefresh
para verificar si se deben generar o actualizar artefactos. Para los artefactos nuevos o actualizados que generaodrefresh
,odsign
calcula una función hash. El resultado de esa compilación de hash se denomina resumen de archivo. Para los artefactos que ya existen,odsign
verifica que los resúmenes de los artefactos existentes coincidan con los resúmenes queodsign
calculó anteriormente. Esto garantiza que no se hayan alterado los artefactos.
En condiciones de error, como cuando el resumen de un archivo no coincide, odrefresh
y odsign
descartan todos los artefactos existentes en /data
y tratan de volver a generarlos. Si eso falla, el sistema vuelve al modo JIT.
odrefresh
y odsign
están protegidos por dm-verity
y forman parte de la cadena de inicio verificado de Android.
Cálculo de resúmenes de archivos con fs-verity
fs-verity es una función del kernel de Linux que realiza la verificación de datos de archivos basada en el árbol de Merkle. Si habilitas fs-verity en un archivo, el sistema de archivos compila un árbol de Merkle sobre los datos del archivo con valores hash SHA-256, lo almacena en una ubicación oculta junto con el archivo y lo marca como de solo lectura. fs-verity verifica automáticamente los datos del archivo en función del árbol de Merkle a pedido a medida que se leen. fs-verity pone a disposición el hash raíz del árbol de Merkle como un valor llamado resumen de archivo de fs-verity y garantiza que los datos que se leen del archivo sean coherentes con este resumen.
odsign
usa fs-verity para mejorar el rendimiento del inicio optimizando la autenticación criptográfica de los artefactos compilados en el dispositivo durante el inicio. Cuando se genera un artefacto, odsign
habilita fs-verity en él. Cuando odsign
verifica un artefacto, verifica el resumen del archivo fs-verity en lugar del hash completo del archivo. Esto quita la necesidad de leer y generar un hash de los datos completos del artefacto durante el inicio. En cambio, fs-verity genera un hash de los datos del artefacto a pedido a medida que se usan, de forma individual.
En los dispositivos cuyo kernel no admite fs-verity, odsign
recurre a la compilación de resúmenes de archivos en el espacio de usuario. odsign
usa el mismo algoritmo de hash basado en el árbol de Merkle que fs-verity, por lo que los resúmenes son los mismos en ambos casos. fs-verity es obligatorio en todos los dispositivos que se lanzaron con Android 11 y versiones posteriores.
Almacenamiento de los resúmenes de archivos
odsign
almacena los resúmenes de archivos de los artefactos en un archivo independiente llamado odsign.info
. Para asegurarse de que no se manipule odsign.info
, odsign.info
se firma con una clave de firma que tiene propiedades de seguridad importantes. En particular, la clave se puede generar y usar solo durante el inicio anticipado, en el que solo se ejecuta código de confianza. Para obtener más información, consulta Claves de firma de confianza.
Verificación de resúmenes de archivos
Cada vez que se inicia el sistema, si odrefresh
determina que los artefactos existentes están actualizados, odsign
se asegura de que no se haya manipulado los archivos desde que se generaron. Para ello, odsign
verifica los resúmenes de archivos. Primero, verifica la firma de odsign.info
. Si la firma es válida, odsign
verifica que el resumen de cada archivo coincida con el resumen correspondiente en odsign.info
.
Claves de firma de confianza
Android 12 introduce una nueva función de Keystore llamada claves de etapa de inicio que aborda las siguientes inquietudes de seguridad:
- ¿Qué impide que un atacante use nuestra clave de firma para firmar su
propia versión de
odsign.info
? - ¿Qué impide que un atacante genere su propia clave de firma y la use para firmar su propia versión de
odsign.info
?
Las claves de la etapa de inicio dividen el ciclo de inicio de Android en niveles y vinculan de forma criptográfica la creación y el uso de una clave a un nivel especificado. odsign
crea su clave de firma en un nivel inicial, cuando solo se ejecuta código de confianza, protegido a través de dm-verity
.
Los niveles de la etapa de inicio se numeran del 0 al número mágico 1000000000. Durante el proceso de inicio de Android, puedes aumentar el nivel de inicio configurando una propiedad del sistema desde init.rc
. Por ejemplo, con el siguiente código, se establece el nivel de inicio en 10:
setprop keystore.boot_level 10
Los clientes de Keystore pueden crear claves vinculadas a un nivel de inicio determinado. Por ejemplo, si creas una clave para el nivel de arranque 10, esa clave solo se puede usar cuando el dispositivo está en el nivel de arranque 10.
odsign
usa el nivel de arranque 30, y la clave de firma que crea está vinculada a ese nivel de arranque. Antes de usar una clave para firmar artefactos, odsign
verifica que la clave esté vinculada al nivel de inicio 30.
Esto evita los dos ataques descritos anteriormente en esta sección:
- Los atacantes no pueden usar la clave generada, ya que, cuando tienen la oportunidad de ejecutar código malicioso, el nivel de inicio aumentó más de 30 y Keystore rechaza las operaciones que usan la clave.
- Los atacantes no pueden crear una clave nueva, ya que, cuando tienen la oportunidad de ejecutar código malicioso, el nivel de inicio aumentó más allá de 30 y Keystore se niega a crear una clave nueva con ese nivel de inicio. Si un atacante crea una clave nueva que no está vinculada al nivel de inicio 30,
odsign
la rechaza.
El almacén de claves garantiza que el nivel de inicio se aplique correctamente. En las siguientes secciones, se explica con más detalle cómo se hace esto para diferentes versiones de Keymaster.
Implementación de Keymaster 4.0
Las diferentes versiones de Keymaster controlan la implementación de las claves de la etapa de inicio de manera diferente. En dispositivos con un TEE/Strongbox de Keymaster 4.0, Keymaster controla la implementación de la siguiente manera:
- En el primer inicio, el almacén de claves crea una clave simétrica K0 con la etiqueta
MAX_USES_PER_BOOT
establecida en1
. Esto significa que la clave solo se puede usar una vez por inicio. - Durante el inicio, si se aumenta el nivel de inicio, se puede generar una clave nueva para ese nivel de inicio a partir de K0 con una función HKDF:
Ki+i=HKDF(Ki, "some_fixed_string")
. Por ejemplo, si pasas del nivel de arranque 0 al nivel de arranque 10, se invoca HKDF 10 veces para derivar K10 de K0. Cuando cambia el nivel de inicio, la clave del nivel de inicio anterior se borra de la memoria y las claves asociadas con los niveles de inicio anteriores ya no están disponibles.
La clave K0 es una clave
MAX_USES_PER_BOOT=1
. Esto significa que también es imposible usar esa clave más adelante en el inicio, ya que siempre se produce al menos una transición de nivel de inicio (al nivel de inicio final).
Cuando un cliente de Keystore, como odsign
, solicita que se cree una clave en el nivel de inicio i
, su BLOB se encripta con la clave Ki
. Como Ki
no está disponible después del nivel de arranque i
, esta clave no se puede crear ni desencriptar en etapas posteriores del arranque.
Implementación de Keymaster 4.1 y KeyMint 1.0
Las implementaciones de Keymaster 4.1 y KeyMint 1.0 son en gran medida iguales a la implementación de Keymaster 4.0. La diferencia principal es que K0 no es una clave MAX_USES_PER_BOOT
, sino una clave EARLY_BOOT_ONLY
, que se introdujo en Keymaster 4.1. Una clave EARLY_BOOT_ONLY
solo se puede usar durante las primeras fases del inicio, cuando no se ejecuta ningún código no confiable. Esto proporciona un nivel adicional de protección: en la implementación de Keymaster 4.0, un atacante que vulnera el sistema de archivos y SELinux puede modificar la base de datos de Keystore para crear su propia clave MAX_USES_PER_BOOT=1
con la que firmar artefactos. Un ataque de este tipo es imposible con las implementaciones de Keymaster 4.1 y KeyMint 1.0, ya que las claves EARLY_BOOT_ONLY
solo se pueden crear durante el inicio anticipado.
Componente público de las claves de firma de confianza
odsign
recupera el componente de clave pública de la clave de firma del almacén de claves.
Sin embargo, Keystore no recupera esa clave pública del TEE/SE que contiene la clave privada correspondiente. En cambio, recupera la clave pública de su propia base de datos en el disco. Esto significa que un atacante que comprometa el sistema de archivos podría modificar la base de datos del almacén de claves para que contenga una clave pública que forme parte de un par de claves pública/privada bajo su control.
Para evitar este ataque, odsign
crea una clave HMAC adicional con el mismo nivel de inicio que la clave de firma. Luego, cuando crea la clave de firma, odsign
usa esta clave HMAC para crear una firma de la clave pública y la almacena en el disco. En los arranques posteriores, cuando se recupera la clave pública de la clave de firma, se usa la clave HMAC para verificar que la firma en el disco coincida con la firma de la clave pública recuperada. Si coinciden, la clave pública es confiable, ya que la clave HMAC solo se puede usar en los primeros niveles de inicio y, por lo tanto, no puede haberla creado un atacante.