Teclas envueltas en hardware

Como la mayoría del software de cifrado de archivos y discos, el cifrado de almacenamiento de Android tradicionalmente se basa en que las claves de cifrado sin procesar están presentes en la memoria del sistema para que se pueda realizar el cifrado. Incluso cuando el cifrado lo realiza un hardware dedicado en lugar de un software, el software generalmente aún necesita administrar las claves de cifrado sin procesar.

Tradicionalmente, esto no se ve como un problema porque las claves no estarán presentes durante un ataque fuera de línea, que es el principal tipo de ataque contra el que el cifrado de almacenamiento pretende proteger. Sin embargo, existe el deseo de brindar una mayor protección contra otros tipos de ataques, como ataques de arranque en frío y ataques en línea en los que un atacante podría perder la memoria del sistema sin comprometer completamente el dispositivo.

Para resolver este problema, Android 11 introdujo soporte para claves envueltas en hardware, donde el soporte de hardware está presente. Las claves envueltas en hardware son claves de almacenamiento que solo se conocen en forma cruda para el hardware dedicado; el software solo ve y trabaja con estas claves en forma envuelta (cifrada). Este hardware debe ser capaz de generar e importar claves de almacenamiento, envolver claves de almacenamiento en formas efímeras y a largo plazo, derivar subclaves, programar directamente una subclave en un motor criptográfico en línea y devolver una subclave separada al software.

Nota : Un motor criptográfico en línea (o hardware de cifrado en línea ) se refiere al hardware que cifra/descifra datos mientras se encuentran en camino hacia/desde el dispositivo de almacenamiento. Por lo general, se trata de un controlador de host UFS o eMMC que implementa las extensiones criptográficas definidas por la especificación JEDEC correspondiente.

Diseño

Esta sección presenta el diseño de la función de claves envueltas en hardware, incluido el soporte de hardware que se requiere para ello. Esta discusión se centra en el cifrado basado en archivos (FBE), pero la solución también se aplica al cifrado de metadatos .

Una forma de evitar la necesidad de claves de cifrado sin procesar en la memoria del sistema sería mantenerlas solo en las ranuras de claves de un motor criptográfico en línea. Sin embargo, este enfoque se encuentra con algunos problemas:

  • El número de claves de cifrado puede exceder el número de ranuras de claves.
  • Los motores criptográficos en línea solo se pueden usar para cifrar/descifrar bloques completos de datos en el disco. Sin embargo, en el caso de FBE, el software aún debe poder realizar otros trabajos criptográficos, como el cifrado de nombres de archivo y la obtención de identificadores de clave. El software aún necesitaría acceso a las claves FBE sin procesar para realizar este otro trabajo.

Para evitar estos problemas, las claves de almacenamiento se convierten en claves encapsuladas en hardware , que solo pueden ser desencapsuladas y utilizadas por hardware dedicado. Esto permite admitir un número ilimitado de claves. Además, la jerarquía de claves se modifica y se traslada parcialmente a este hardware, lo que permite devolver una subclave al software para tareas que no pueden usar un motor criptográfico en línea.

Jerarquía de claves

Las claves se pueden derivar de otras claves utilizando un KDF (función de derivación de claves) como HKDF , lo que da como resultado una jerarquía de claves .

El siguiente diagrama muestra una jerarquía de claves típica para FBE cuando no se utilizan claves envueltas en hardware:

Jerarquía de claves FBE (estándar)
Figura 1. Jerarquía de claves de FBE (estándar)

La clave de clase FBE es la clave de cifrado sin procesar que Android pasa al kernel de Linux para desbloquear un conjunto particular de directorios cifrados, como el almacenamiento cifrado de credenciales para un usuario de Android en particular. (En el kernel, esta clave se denomina clave maestra fscrypt ). A partir de esta clave, el kernel deriva las siguientes subclaves:

  • El identificador clave. Esto no se usa para el cifrado, sino que es un valor que se usa para identificar la clave con la que se protege un archivo o directorio en particular.
  • La clave de cifrado del contenido del archivo
  • La clave de cifrado de nombres de archivo

Por el contrario, el siguiente diagrama muestra la jerarquía de claves para FBE cuando se utilizan claves envueltas en hardware:

Jerarquía de claves FBE (con clave envuelta en hardware)
Figura 2. Jerarquía de claves FBE (con clave envuelta en hardware)

En comparación con el caso anterior, se agregó un nivel adicional a la jerarquía de claves y se reubicó la clave de cifrado del contenido del archivo. El nodo raíz aún representa la clave que Android pasa a Linux para desbloquear un conjunto de directorios encriptados. Sin embargo, ahora esa clave está envuelta de forma efímera y, para poder usarla, debe pasarse a un hardware dedicado. Este hardware debe implementar dos interfaces que toman una clave envuelta efímeramente:

  • Una interfaz para derivar inline_encryption_key y programarla directamente en una ranura de clave del motor criptográfico en línea. Esto permite cifrar/descifrar el contenido del archivo sin que el software tenga acceso a la clave sin procesar. En los núcleos comunes de Android, esta interfaz corresponde a la operación blk_ksm_ll_ops::keyslot_program , que debe implementar el controlador de almacenamiento.
  • Una interfaz para derivar y devolver sw_secret ("secreto de software", también llamado "secreto sin procesar" en algunos lugares), que es la clave que utiliza Linux para derivar las subclaves para todo lo que no sea el cifrado del contenido del archivo. En los núcleos comunes de Android, esta interfaz corresponde a la operación blk_ksm_ll_ops::derive_raw_secret , que debe implementar el controlador de almacenamiento.

Para derivar inline_encryption_key y sw_secret de la clave de almacenamiento sin procesar, el hardware debe usar un KDF criptográficamente fuerte. Este KDF debe seguir las mejores prácticas de criptografía; debe tener un nivel de seguridad de al menos 256 bits, es decir, suficiente para cualquier algoritmo que se utilice posteriormente. También debe usar una etiqueta distinta, contexto y/o cadena de información específica de la aplicación al derivar cada tipo de subclave para garantizar que las subclaves resultantes estén criptográficamente aisladas, es decir, el conocimiento de una de ellas no revela ninguna otra. No es necesario ampliar la clave, ya que la clave de almacenamiento sin procesar ya es una clave uniformemente aleatoria.

Técnicamente, se podría utilizar cualquier KDF que cumpla con los requisitos de seguridad. Sin embargo, con fines de prueba, es necesario volver a implementar el mismo KDF en el código de prueba. Actualmente, se ha revisado e implementado un KDF; se puede encontrar en el código fuente de vts_kernel_encryption_test . Se recomienda que el hardware utilice este KDF, que utiliza NIST SP 800-108 "KDF en modo contador" con AES-256-CMAC como PRF. Tenga en cuenta que para que sea compatible, todas las partes del algoritmo deben ser idénticas, incluida la elección de contextos y etiquetas KDF para cada subclave.

Envoltura de llaves

Para cumplir con los objetivos de seguridad de las claves encapsuladas en hardware, se definen dos tipos de encapsulado de claves:

  • Envoltura efímera : el hardware encripta la clave sin procesar utilizando una clave que se genera aleatoriamente en cada arranque y no se expone directamente fuera del hardware.
  • Envoltura a largo plazo : el hardware encripta la clave sin procesar utilizando una clave única y persistente integrada en el hardware que no está expuesta directamente fuera del hardware.

Todas las claves que se pasan al kernel de Linux para desbloquear el almacenamiento se empaquetan de forma efímera. Esto garantiza que si un atacante puede extraer una clave en uso de la memoria del sistema, esa clave no solo se podrá usar fuera del dispositivo, sino también dentro del dispositivo después de un reinicio.

Al mismo tiempo, Android aún necesita poder almacenar una versión cifrada de las claves en el disco para que puedan desbloquearse en primer lugar. Las claves sin procesar funcionarían para este propósito. Sin embargo, es deseable que las claves sin procesar nunca estén presentes en la memoria del sistema para que nunca se puedan extraer para usarlas fuera del dispositivo, incluso si se extraen en el momento del arranque. Por ello, se define el concepto de envoltura a largo plazo.

Para admitir la gestión de claves envueltas en estas dos formas diferentes, el hardware debe implementar las siguientes interfaces:

  • Interfaces para generar e importar claves de almacenamiento, devolviéndolas en formato empaquetado a largo plazo. A estas interfaces se accede indirectamente a través de KeyMint y corresponden a la etiqueta TAG_STORAGE_KEY KeyMint. vold usa la capacidad de "generar" para generar nuevas claves de almacenamiento para que las use Android, mientras que vts_kernel_encryption_test usa la capacidad de "importar" para importar claves de prueba.
  • Una interfaz para convertir una clave de almacenamiento encapsulada a largo plazo en una clave de almacenamiento encapsulada efímeramente. Esto corresponde al método convertStorageKeyToEphemeral KeyMint. Este método lo utilizan tanto vold como vts_kernel_encryption_test para desbloquear el almacenamiento.

El algoritmo de envoltura de claves es un detalle de implementación, pero debe usar un AEAD fuerte como AES-256-GCM con IV aleatorios.

Se requieren cambios de software

AOSP ya tiene un marco básico para admitir claves envueltas en hardware. Esto incluye la compatibilidad con componentes del espacio de usuario como vold , así como la compatibilidad con el kernel de Linux en blk-crypto , fscrypt y dm-default-key .

Sin embargo, se requieren algunos cambios específicos de la implementación.

Cambios de KeyMint

La implementación de KeyMint del dispositivo debe modificarse para admitir TAG_STORAGE_KEY e implementar el método convertStorageKeyToEphemeral .

En Keymaster, se utilizó exportKey en lugar de convertStorageKeyToEphemeral .

Cambios en el kernel de Linux

El controlador del kernel de Linux para el motor criptográfico en línea del dispositivo debe modificarse para configurar BLK_CRYPTO_FEATURE_WRAPPED_KEYS , hacer que las keyslot_program() y keyslot_evict() admitan la programación/desalojar claves envueltas en hardware e implementar la operación derive_raw_secret() .

Pruebas

Aunque el cifrado con claves envueltas en hardware es más difícil de probar que el cifrado con claves estándar, todavía es posible probarlo importando una clave de prueba y volviendo a implementar la derivación de clave que hace el hardware. Esto se implementa en vts_kernel_encryption_test . Para ejecutar esta prueba, ejecute:

atest -v vts_kernel_encryption_test

Lea el registro de prueba y verifique que los casos de prueba clave envueltos en hardware (p. ej., FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy y DmDefaultKeyTest.TestHwWrappedKey ) no se omitieron debido a que no se detectó la compatibilidad con claves envueltas en hardware, ya que los resultados de la prueba seguirán siendo "aprobados" en Ese caso.

Habilitación

Una vez que la compatibilidad con la clave envuelta en hardware del dispositivo funcione correctamente, puede realizar los siguientes cambios en el archivo fstab del dispositivo para que Android lo use para el cifrado de metadatos y FBE:

  • FBE: agregue el indicador wrappedkey_v0 al parámetro de cifrado de fileencryption . Por ejemplo, use fileencryption=::inlinecrypt_optimized+wrappedkey_v0 . Para obtener más detalles, consulte la documentación de FBE .
  • Cifrado de metadatos: agregue el indicador wrappedkey_v0 al parámetro metadata_encryption . Por ejemplo, use metadata_encryption=:wrappedkey_v0 . Para obtener más detalles, consulte la documentación de cifrado de metadatos .