Al igual que la mayoría de los software de encriptación de discos y archivos, la encriptación de almacenamiento de Android tradicionalmente se basa en que las claves de encriptación sin procesar estén presentes en la memoria del sistema para que se pueda realizar la encriptación. Incluso cuando la encriptación se realiza por un hardware dedicado y no por software, el software, por lo general, debe administrar las claves de encriptación sin procesar.
Por lo general, esto no se ve como un problema, ya que las claves no estarán presentes. durante un ataque sin conexión, que es el principal tipo de ataque que cómo se protege la encriptación. Sin embargo, se desea proporcionar una mayor protección contra otros tipos de ataques, como los ataques de inicio en frío y los ataques en línea en los que un atacante podría filtrar la memoria del sistema sin vulnerar por completo el dispositivo.
Para resolver este problema, Android 11 introdujo la compatibilidad con llaves empaquetadas en hardware, donde hay compatibilidad con el hardware. Las claves empaquetadas en hardware son claves de almacenamiento que solo se conocen en formato sin procesar para el hardware dedicado. El software solo ve estas claves y funciona con ellas en formato empaquetado (encriptado). Este hardware debe ser capaz de generar e importar claves de almacenamiento, unirlas en formas efímeras y a largo plazo, del usuario, programar directamente una subclave en un motor criptográfico intercalado y mostrar una subclave independiente al software.
Nota: Un motor criptográfico intercalado (o intercalado) hardware de encriptación) se refiere al hardware que encripta/desencripta datos mientras cuando está en camino hacia o desde el dispositivo de almacenamiento. Por lo general, es un host de UFS o eMMC controlador que implemente las extensiones criptográficas definidas por el servicio correspondiente JEDEC.
Diseño
En esta sección, se presenta el diseño de la función de claves unidas en hardware, que incluye lo siguiente: y qué soporte de hardware se necesita. Este análisis se centra en la encriptación basada en archivos (FBE), pero que la solución se aplica a los metadatos encriptación.
Una forma de evitar la necesidad de las claves de encriptación sin procesar en la memoria del sistema sería guardarlas solo en los espacios de claves de un motor de criptografía intercalado. Sin embargo, este enfoque tiene algunos problemas:
- La cantidad de claves de encriptación puede exceder la cantidad de ranuras de claves.
- Los motores criptográficos intercalados solo se pueden usar para encriptar o desencriptar bloques completos de de los datos en el disco. Sin embargo, en el caso de los FBE, el software debe poder hacer otras tareas criptográficas, como la encriptación de nombres de archivo y las claves derivadas identificadores. El software aún necesitaría acceso a las claves FBE sin procesar para realizar esta otra tarea.
Para evitar estos problemas, las claves de almacenamiento se convierten en llaves unidas por hardware, que solo las pueden usar o hardware específico. Esto permite que se admita una cantidad ilimitada de claves. En Además, la jerarquía de claves se modifica y se mueve parcialmente a este hardware, que permite devolver una subclave al software para tareas que no pueden usar una con un motor criptográfico intercalado.
Jerarquía de claves
Las claves se pueden derivar de otras claves con 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 típica jerarquía de claves para FBE cuando claves unidas con hardware no se usan:
La clave de clase FBE es la clave de encriptación sin procesar que Android pasa al kernel de Linux para desbloquear un conjunto particular de directorios encriptados, como el almacenamiento encriptado con credenciales de un usuario de Android en particular. (En el kernel, este se denomina clave maestra de fscrypt). A partir de esta clave, el kernel deriva las siguientes subclaves:
- Es el identificador de la clave. No se usa para la encriptación, 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 encriptación del contenido del archivo
- La clave de encriptación del nombre de archivo
Por el contrario, el siguiente diagrama muestra la jerarquía de claves de FBE cuando claves unidas con hardware:
En comparación con el caso anterior, se agregó un nivel adicional a la clave y la clave de encriptación del contenido del archivo se reubica. El nodo raíz sigue representando la clave que Android pasa a Linux para desbloquear un conjunto de directorios encriptados. Sin embargo, ahora esa clave está en forma de unión efímera y, para usarse, se debe pasar al hardware dedicado. Este hardware debe implementar dos interfaces que tomen una clave unida de forma efímera:
- Una interfaz para derivar
inline_encryption_key
y programarlo en una ranura del motor criptográfico intercalado. Esto permite que el contenido de los archivos se encripte o desencripte sin que el software tenga acceso a la clave sin procesar. En los kernels comunes de Android, esta interfaz corresponde a la operaciónblk_crypto_ll_ops::keyslot_program
, que el controlador de almacenamiento debe implementar. - Una interfaz para derivar y mostrar
sw_secret
("software Secret" también denominado "Secreto sin procesar" en algunos lugares), que es la clave que Linux los usa para derivar las subclaves de todo lo que no sea el contenido del archivo. la encriptación. En los kernels comunes de Android, esta interfaz corresponde a la operaciónblk_crypto_ll_ops::derive_sw_secret
, que el controlador de almacenamiento debe implementar.
Para derivar inline_encryption_key
y sw_secret
de la
sin procesar, el hardware debe usar un KDF seguro a nivel criptográfico. Este KDF debe seguir las prácticas recomendadas de criptografía y tener una seguridad de al menos 256 bits, es decir, suficiente para cualquier algoritmo que se use más adelante. También debe usar una etiqueta, un contexto y una cadena de información específica de la app distinta cuando derive cada tipo de subclave para garantizar que las subclaves resultantes estén aisladas criptográficamente, es decir, que el conocimiento de una de ellas no revele ninguna otra. No es necesario extender la clave, dado que la clave de almacenamiento sin procesar ya es un
uniformemente aleatoria.
Técnicamente, se puede usar cualquier KDF que cumpla con los requisitos de seguridad.
Sin embargo, para realizar pruebas, es necesario volver a implementar el mismo KDF en
código de prueba. Actualmente, se revisó y se implementó un KDF. se puede encontrar
en el código fuente de vts_kernel_encryption_test
.
Se recomienda que el hardware use este KDF, que usa NIST SP 800-108 "KDF en modo contador" con AES-256-CMAC como PRF. Ten en cuenta que, para ser compatibles, todas
partes del algoritmo deben ser idénticas, incluida la elección de contextos de KDF
y etiquetas para cada subclave.
Unión de claves
Para cumplir con los objetivos de seguridad de las claves unidas con hardware, hay dos tipos de unión de claves están definidos:
- Unión efímera: El hardware encripta la clave sin procesar con una clave que se genera de forma aleatoria en cada inicio y que no se expone directamente fuera del hardware.
- Unión a largo plazo: el hardware encripta la clave sin procesar con un una clave única y persistente integrada en el hardware que no está directamente expuestas por fuera del hardware.
Todas las claves que se pasan al kernel de Linux para desbloquear el almacenamiento se unen de forma efímera. Esto garantiza que si un atacante puede extraer clave en uso de la memoria del sistema, esta quedará inutilizable no solo sino también en el dispositivo después de reiniciarlo.
Al mismo tiempo, Android aún debe poder almacenar una versión encriptada de las claves en el disco para que se puedan desbloquear en primer lugar. Las claves sin procesar funcionarían para este propósito. Sin embargo, es conveniente que las claves sin procesar nunca estén presentes en la memoria del sistema para que nunca se puedan extraer y usar fuera del dispositivo, incluso si se extraen durante el inicio. Por este motivo, se define el concepto de vinculación a largo plazo.
Para admitir la administración de claves unidas de estas dos formas diferentes, el hardware debe implementa las siguientes interfaces:
- Interfaces para generar e importar claves de almacenamiento, que las devuelven en
formato integrado a largo plazo. Se accede a estas interfaces indirectamente
KeyMint y corresponden a la etiqueta de KeyMint
TAG_STORAGE_KEY
. La columna "generate"vold
usa la función para generar nuevo almacenamiento claves para usar en Android, mientras que el comando "import" la capacidad se usa porvts_kernel_encryption_test
para importar claves de prueba. - Una interfaz para convertir una clave de almacenamiento unido a largo plazo en un
una clave de almacenamiento unida de forma efímera. Esto corresponde al método KeyMint
convertStorageKeyToEphemeral
.vold
yvts_kernel_encryption_test
usan este método para desbloquear el almacenamiento.
El algoritmo de unión de claves es un detalle de implementación, pero debe usar un AEAD sólido, como AES-256-GCM con IVs aleatorios.
Se requieren cambios en el software
AOSP ya tiene un framework básico para admitir claves empaquetadas 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 en 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
Se debe modificar el controlador de kernel de Linux para el motor de criptografía intercalado del dispositivo para admitir claves empaquetadas en hardware.
Para kernels android14
y versiones posteriores,
establecer BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
en blk_crypto_profile::key_types_supported
,
marca blk_crypto_ll_ops::keyslot_program
y blk_crypto_ll_ops::keyslot_evict
admitir la programación y la expulsión de claves unidas a hardware,
y, luego, implementar blk_crypto_ll_ops::derive_sw_secret
.
Para los kernels android12
y android13
,
establecer BLK_CRYPTO_FEATURE_WRAPPED_KEYS
en blk_keyslot_manager::features
,
marca blk_ksm_ll_ops::keyslot_program
y blk_ksm_ll_ops::keyslot_evict
admitir la programación y la expulsión de claves unidas a hardware,
y, luego, implementar blk_ksm_ll_ops::derive_raw_secret
.
Para los kernels android11
,
establecer BLK_CRYPTO_FEATURE_WRAPPED_KEYS
en keyslot_manager::features
,
marca keyslot_mgmt_ll_ops::keyslot_program
y keyslot_mgmt_ll_ops::keyslot_evict
admitir la programación y la expulsión de claves unidas a hardware,
y, luego, implementar keyslot_mgmt_ll_ops::derive_raw_secret
.
Prueba
Aunque la encriptación con claves unidas en hardware es más difícil de probar que la encriptación
con claves estándar, es posible realizar pruebas importando una clave de prueba y
o volver a implementar la derivación
de claves que hace el hardware. Esto se implementa
en vts_kernel_encryption_test
. Para ejecutar esta prueba, ejecuta lo siguiente:
atest -v vts_kernel_encryption_test
Lee el registro de prueba y verifica que no se hayan omitido los casos de prueba de claves empaquetadas en hardware (por ejemplo, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
y DmDefaultKeyTest.TestHwWrappedKey
) debido a que no se detectó la compatibilidad con las claves empaquetadas en hardware, ya que los resultados de la prueba aún se "aprueban" en ese caso.
Habilita las teclas
Una vez que la compatibilidad de la clave unida en hardware del dispositivo funcione correctamente, puedes
haz los siguientes cambios en el archivo fstab
del dispositivo
Android lo usa para la encriptación de metadatos y FBE:
- FBE: agrega la marca
wrappedkey_v0
al Parámetrofileencryption
. Por ejemplo, usafileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Para más detalles, consulta la FBE documentación. - Encriptación de metadatos: Agrega la marca
wrappedkey_v0
al parámetrometadata_encryption
. Por ejemplo, usametadata_encryption=:wrappedkey_v0
Para obtener más detalles, consulta la metadatos documentación de encriptación.