Como la mayoría del software de cifrado de discos y archivos, el cifrado de almacenamiento de Android tradicionalmente se basa en que las claves de cifrado sin formato estén presentes en la memoria del sistema para que se pueda realizar el cifrado. Incluso cuando el cifrado se realiza mediante hardware dedicado en lugar de software, el software generalmente aún necesita administrar las claves de cifrado sin procesar.
Tradicionalmente, esto no se considera 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 filtrar la memoria del sistema sin comprometer completamente el dispositivo.
Para resolver este problema, Android 11 introdujo soporte para claves empaquetadas en hardware , donde el soporte de hardware está presente. Las claves empaquetadas en hardware son claves de almacenamiento que solo el hardware dedicado conoce en su forma original; El software solo ve y trabaja con estas claves en formato empaquetado (cifrado). Este hardware debe ser capaz de generar e importar claves de almacenamiento, empaquetar 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 de cifrado en línea (o hardware de cifrado en línea ) se refiere a hardware que cifra/descifra datos mientras están 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 empaquetadas 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 formato en la memoria del sistema sería mantenerlas sólo en las ranuras de claves de un motor de cifrado en línea. Sin embargo, este enfoque presenta algunos problemas:
- La cantidad de claves de cifrado puede exceder la cantidad de ranuras de claves.
- Los motores de cifrado en línea solo se pueden utilizar 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 archivos y la obtención de identificadores de claves. El software aún necesitaría acceso a las claves FBE sin procesar para poder realizar este otro trabajo.
Para evitar estos problemas, las claves de almacenamiento se convierten en claves envueltas en hardware , que solo pueden ser desenvueltas y utilizadas por hardware dedicado. Esto permite admitir una cantidad ilimitada 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 utilizar un motor criptográfico en línea.
Jerarquía clave
Las claves se pueden derivar de otras claves utilizando una 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 empaquetadas en hardware:
La clave de clase FBE es la clave de cifrado sin formato que Android pasa al kernel de Linux para desbloquear un conjunto particular de directorios cifrados, como el almacenamiento cifrado con credenciales para un usuario de Android en particular. (En el kernel, esta clave se llama clave maestra fscrypt ). A partir de esta clave, el kernel deriva las siguientes subclaves:
- El identificador de clave. Esto no se utiliza para el cifrado, sino que es un valor que se utiliza 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 archivos
Por el contrario, el siguiente diagrama muestra la jerarquía de claves para FBE cuando se utilizan claves empaquetadas 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 todavía representa la clave que Android pasa a Linux para desbloquear un conjunto de directorios cifrados. Sin embargo, ahora esa clave está envuelta de forma efímera y, para poder usarse, se debe pasar a un hardware dedicado. Este hardware debe implementar dos interfaces que acepten 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 formato. En los kernels comunes de Android, esta interfaz corresponde a la operaciónblk_crypto_ll_ops::keyslot_program
, que debe ser implementada por el controlador de almacenamiento. - Una interfaz para derivar y devolver
sw_secret
("secreto de software", también llamado "secreto sin formato" en algunos lugares), que es la clave que Linux usa para derivar las subclaves para todo lo que no sea el cifrado del contenido del archivo. En los kernels comunes de Android, esta interfaz corresponde a la operaciónblk_crypto_ll_ops::derive_sw_secret
, que debe ser implementada por el controlador de almacenamiento.
Para derivar inline_encryption_key
y sw_secret
de la clave de almacenamiento sin formato, el hardware debe utilizar un KDF criptográficamente sólido. Este KDF debe seguir las mejores prácticas de criptografía; debe tener una seguridad de al menos 256 bits, es decir, suficiente para cualquier algoritmo utilizado posteriormente. También debe utilizar una etiqueta distinta, un contexto y/o una 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 formato ya es una clave uniformemente aleatoria.
Técnicamente, se podría utilizar cualquier KDF que cumpla con los requisitos de seguridad. Sin embargo, para 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 ser 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 empaquetadas en hardware, se definen dos tipos de envoltura de claves:
- Envoltura efímera : el hardware cifra la clave sin formato utilizando una clave que se genera aleatoriamente en cada arranque y no se expone directamente fuera del hardware.
- Envoltura a largo plazo : el hardware cifra la clave sin formato utilizando una clave única y persistente integrada en el hardware que no está expuesta directamente fuera del hardware.
Todas las claves pasadas al kernel de Linux para desbloquear el almacenamiento están envueltas efímeramente. Esto garantiza que si un atacante puede extraer una clave en uso de la memoria del sistema, esa clave quedará inutilizable no solo 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 poder desbloquearlas en primer lugar. Las claves sin formato funcionarían para este propósito. Sin embargo, es deseable que las claves sin formato nunca estén presentes en la memoria del sistema para que nunca puedan extraerse para usarse fuera del dispositivo, incluso si se extraen en el momento del arranque. Por este motivo se define el concepto de envoltura de larga duración.
Para admitir la gestión de claves empaquetadas de estas dos formas diferentes, el hardware debe implementar las siguientes interfaces:
- Interfaces para generar e importar claves de almacenamiento, devolviéndolas en forma empaquetada a largo plazo. Se accede a estas interfaces indirectamente a través de KeyMint y corresponden a la etiqueta
TAG_STORAGE_KEY
KeyMint.vold
utiliza la capacidad de "generar" para generar nuevas claves de almacenamiento para su uso en Android, mientras quevts_kernel_encryption_test
utiliza la capacidad de "importar" para importar claves de prueba. - Una interfaz para convertir una clave de almacenamiento empaquetada a largo plazo en una clave de almacenamiento empaquetada efímeramente. Esto corresponde al método
convertStorageKeyToEphemeral
KeyMint. Este método lo utilizan tantovold
comovts_kernel_encryption_test
para desbloquear el almacenamiento.
El algoritmo de ajuste de claves es un detalle de implementación, pero debería utilizar un AEAD potente como AES-256-GCM con IV aleatorios.
Se requieren cambios de software
AOSP ya tiene un marco básico para admitir claves empaquetadas en hardware. Esto incluye el soporte en componentes del espacio de usuario como vold
, así como el soporte del 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 usó 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 admitir claves empaquetadas en hardware.
Para kernels android14
y superiores, configure BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
en blk_crypto_profile::key_types_supported
, haga que blk_crypto_ll_ops::keyslot_program
y blk_crypto_ll_ops::keyslot_evict
admitan la programación/desalojamiento de claves empaquetadas en hardware e implemente blk_crypto_ll_ops::derive_sw_secret
.
Para los kernels android12
y android13
, configure BLK_CRYPTO_FEATURE_WRAPPED_KEYS
en blk_keyslot_manager::features
, haga que blk_ksm_ll_ops::keyslot_program
y blk_ksm_ll_ops::keyslot_evict
admitan la programación/desalojamiento de claves empaquetadas en hardware e implemente blk_ksm_ll_ops::derive_raw_secret
.
Para los kernels android11
, configure BLK_CRYPTO_FEATURE_WRAPPED_KEYS
keyslot_manager::features
, haga keyslot_mgmt_ll_ops::keyslot_program
keyslot_mgmt_ll_ops::keyslot_evict
admitan la programación/desalojamiento de claves empaquetadas en hardware e implementekeyslot_mgmt_ll_ops keyslot_mgmt_ll_ops::derive_raw_secret
.
Pruebas
Aunque el cifrado con claves empaquetadas en hardware es más difícil de probar que el cifrado con claves estándar, aún es posible realizar la prueba importando una clave de prueba y volviendo a implementar la derivación de clave que realiza 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 de claves empaquetadas en hardware (por ejemplo, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
y DmDefaultKeyTest.TestHwWrappedKey
) no se omitieron debido a que no se detectó la compatibilidad con claves empaquetadas en hardware, ya que los resultados de la prueba aún se "aprobarán" en Ese caso.
Habilitando
Una vez que la compatibilidad con claves empaquetadas en hardware del dispositivo funcione correctamente, puede realizar los siguientes cambios en el archivo fstab
del dispositivo para que Android lo use para FBE y cifrado de metadatos:
- FBE: agregue el indicador
wrappedkey_v0
al parámetrofileencryption
. Por ejemplo, utilicefileencryption=::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ámetrometadata_encryption
. Por ejemplo, utilicemetadata_encryption=:wrappedkey_v0
. Para obtener más detalles, consulte la documentación sobre cifrado de metadatos .