Как и большинство программ для шифрования дисков и файлов, шифрование хранилища Android традиционно полагается на наличие необработанных ключей шифрования в системной памяти, чтобы можно было выполнить шифрование. Даже когда шифрование выполняется выделенным оборудованием, а не программным обеспечением, программному обеспечению обычно все равно необходимо управлять необработанными ключами шифрования.
Это традиционно не рассматривается как проблема, поскольку ключи не будут присутствовать во время офлайн-атаки, которая является основным типом атаки, от которой шифрование хранилища призвано защищать. Однако есть желание обеспечить повышенную защиту от других типов атак, таких как атаки с холодной загрузкой и онлайн-атаки, когда злоумышленник может получить утечку системной памяти без полной компрометации устройства.
Чтобы решить эту проблему, Android 11 представила поддержку аппаратно-обернутых ключей , где присутствует аппаратная поддержка. Аппаратно-обернутые ключи — это ключи хранения, которые известны только в сыром виде выделенному оборудованию; программное обеспечение видит и работает с этими ключами только в обернутой (зашифрованной) форме. Это оборудование должно иметь возможность генерировать и импортировать ключи хранения, оборачивать ключи хранения в эфемерные и долгосрочные формы, выводить подключи, напрямую программировать один подключ во встроенный криптографический движок и возвращать отдельный подключ программному обеспечению.
Примечание: Встроенный криптодвижок (или встроенное криптооборудование ) относится к оборудованию, которое шифрует/дешифрует данные, пока они находятся на пути к/с устройства хранения. Обычно это хост-контроллер UFS или eMMC, который реализует крипторасширения, определенные соответствующей спецификацией JEDEC.
Дизайн
В этом разделе представлена конструкция функции аппаратно-обернутых ключей, включая то, какая аппаратная поддержка требуется для нее. Это обсуждение фокусируется на файловом шифровании (FBE), но решение применимо и к шифрованию метаданных .
Один из способов избежать необходимости в необработанных ключах шифрования в системной памяти — хранить их только в слотах ключей встроенного криптодвижка. Однако этот подход сталкивается с некоторыми проблемами:
- Количество ключей шифрования может превышать количество слотов ключей.
- Встроенные криптодвижки обычно теряют содержимое своих слотов ключей, если контроллер хранилища (обычно UFS или eMMC) сбрасывается. Сброс контроллера хранилища — это стандартная процедура восстановления после ошибок, которая выполняется при возникновении определенных типов ошибок хранилища, и такие ошибки могут возникнуть в любое время. Поэтому при использовании встроенного шифрования операционная система всегда должна быть готова перепрограммировать слоты ключей без вмешательства пользователя.
- Встроенные криптодвижки могут использоваться только для шифрования/дешифрования полных блоков данных на диске. Однако в случае FBE программное обеспечение все еще должно иметь возможность выполнять другую криптографическую работу, такую как шифрование имен файлов и получение идентификаторов ключей. Программному обеспечению все еще необходим доступ к необработанным ключам FBE для выполнения этой другой работы.
Чтобы избежать этих проблем, ключи хранения вместо этого превращаются в аппаратно-обернутые ключи , которые могут быть распакованы и использованы только выделенным оборудованием. Это позволяет поддерживать неограниченное количество ключей. Кроме того, иерархия ключей изменяется и частично перемещается на это оборудование, что позволяет возвращать подключ в программное обеспечение для задач, которые не могут использовать встроенный криптодвижок.
Иерархия ключей
Ключи могут быть получены из других ключей с использованием KDF (функции вывода ключей) , например HKDF , что приводит к иерархии ключей .
На следующей диаграмме изображена типичная иерархия ключей для FBE, когда аппаратно защищенные ключи не используются:
Ключ класса FBE — это необработанный ключ шифрования, который Android передает ядру Linux для разблокировки определенного набора зашифрованных каталогов, например, хранилища с зашифрованными учетными данными для определенного пользователя Android. (В ядре этот ключ называется главным ключом fscrypt .) Из этого ключа ядро выводит следующие подключи:
- Идентификатор ключа. Он не используется для шифрования, а представляет собой значение, используемое для идентификации ключа, с помощью которого защищен конкретный файл или каталог.
- Ключ шифрования содержимого файла
- Ключ шифрования имен файлов
Напротив, следующая диаграмма отображает иерархию ключей для FBE при использовании аппаратно защищенных ключей:
По сравнению с предыдущим случаем, в иерархию ключей был добавлен дополнительный уровень, а ключ шифрования содержимого файла был перемещен. Корневой узел по-прежнему представляет собой ключ, который Android передает Linux для разблокировки набора зашифрованных каталогов. Однако теперь этот ключ находится в форме эфемерной оболочки, и для использования его необходимо передать на выделенное оборудование. Это оборудование должно реализовать два интерфейса, которые принимают эфемерно упакованный ключ:
- Один интерфейс для получения
inline_encryption_key
и его прямого программирования в keyslot встроенного криптодвижка. Это позволяет шифровать/расшифровывать содержимое файлов без доступа программного обеспечения к необработанному ключу. В общих ядрах Android этот интерфейс соответствует операцииblk_crypto_ll_ops::keyslot_program
, которая должна быть реализована драйвером хранилища. - Один интерфейс для получения и возврата
sw_secret
(«программный секрет» — также называемый в некоторых местах «сырой секрет»), который является ключом, используемым Linux для получения подключей для всего, кроме шифрования содержимого файлов. В общих ядрах Android этот интерфейс соответствует операцииblk_crypto_ll_ops::derive_sw_secret
, которая должна быть реализована драйвером хранилища.
Чтобы вывести inline_encryption_key
и sw_secret
из необработанного ключа хранения, оборудование должно использовать криптографически стойкий KDF. Этот KDF должен следовать лучшим практикам криптографии; он должен иметь уровень безопасности не менее 256 бит, то есть достаточный для любого алгоритма, используемого в дальнейшем. Он также должен использовать отдельную метку, контекст и строку информации, специфичную для приложения, при выведении каждого типа подключа, чтобы гарантировать, что полученные подключаемые ключи криптографически изолированы, то есть знание одного из них не раскрывает никакой другой. Растягивание ключа не требуется, так как необработанный ключ хранения уже является равномерно случайным ключом.
Технически можно использовать любой KDF, отвечающий требованиям безопасности. Однако для целей тестирования необходимо повторно реализовать тот же KDF в тестовом коде. В настоящее время один KDF был рассмотрен и реализован; его можно найти в исходном коде vts_kernel_encryption_test
. Рекомендуется, чтобы оборудование использовало этот KDF, который использует NIST SP 800-108 "KDF в режиме счетчика" с AES-256-CMAC в качестве PRF. Обратите внимание, что для совместимости все части алгоритма должны быть идентичны, включая выбор контекстов KDF и меток для каждого подключа.
Упаковка ключей
Для достижения целей безопасности аппаратно упакованных ключей определены два типа упаковки ключей:
- Эфемерная упаковка : оборудование шифрует необработанный ключ с помощью ключа, который генерируется случайным образом при каждой загрузке и не раскрывается напрямую за пределами оборудования.
- Долгосрочное зашифрование : оборудование шифрует необработанный ключ, используя уникальный постоянный ключ, встроенный в оборудование, который не раскрывается напрямую за пределами оборудования.
Все ключи, переданные ядру Linux для разблокировки хранилища, эфемерно завернуты. Это гарантирует, что если злоумышленник сможет извлечь используемый ключ из системной памяти, то этот ключ будет непригоден для использования не только вне устройства, но и на нем после перезагрузки.
В то же время Android по-прежнему должен иметь возможность хранить зашифрованную версию ключей на диске, чтобы их можно было разблокировать в первую очередь. Необработанные ключи подойдут для этой цели. Однако желательно, чтобы необработанные ключи вообще никогда не присутствовали в системной памяти, чтобы их никогда нельзя было извлечь для использования вне устройства, даже если они извлечены во время загрузки. По этой причине определена концепция долгосрочного обертывания.
Для поддержки управления ключами, упакованными этими двумя различными способами, оборудование должно реализовывать следующие интерфейсы:
- Интерфейсы для генерации и импорта ключей хранения, возвращая их в долгосрочной обернутой форме. Доступ к этим интерфейсам осуществляется косвенно через KeyMint, и они соответствуют тегу
TAG_STORAGE_KEY
KeyMint. Возможность "generate" используетсяvold
для генерации новых ключей хранения для использования Android, в то время как возможность "import" используетсяvts_kernel_encryption_test
для импорта тестовых ключей. - Интерфейс для преобразования долгосрочного завернутого ключа хранения в эфемерно завернутый ключ хранения. Это соответствует методу
convertStorageKeyToEphemeral
KeyMint. Этот метод используется какvold
, так иvts_kernel_encryption_test
для разблокировки хранилища.
Алгоритм упаковки ключей — это деталь реализации, но он должен использовать надежный AEAD, такой как AES-256-GCM со случайными IV.
Требуются изменения в программном обеспечении
AOSP уже имеет базовую структуру для поддержки аппаратно-обернутых ключей. Это включает поддержку в компонентах пользовательского пространства, таких как vold
, а также поддержку ядра Linux в blk-crypto , fscrypt и dm-default-key .
Однако необходимы некоторые изменения, специфичные для реализации.
Изменения KeyMint
Реализацию KeyMint на устройстве необходимо изменить для поддержки TAG_STORAGE_KEY
и реализации метода convertStorageKeyToEphemeral
.
В Keymaster вместо convertStorageKeyToEphemeral
использовался exportKey
.
Изменения ядра Linux
Драйвер ядра Linux для встроенного криптографического движка устройства должен быть изменен для поддержки аппаратно защищенных ключей.
Для ядер android14
и выше установите BLK_CRYPTO_KEY_TYPE_HW_WRAPPED
в blk_crypto_profile::key_types_supported
, включите поддержку программирования/изъятия аппаратно защищенных ключей blk_crypto_ll_ops::keyslot_program
и blk_crypto_ll_ops::keyslot_evict
и реализуйте blk_crypto_ll_ops::derive_sw_secret
.
Для ядер android12
и android13
установите BLK_CRYPTO_FEATURE_WRAPPED_KEYS
в blk_keyslot_manager::features
, включите поддержку программирования/удаления аппаратно упакованных ключей blk_ksm_ll_ops::keyslot_program
и blk_ksm_ll_ops::keyslot_evict
и реализуйте blk_ksm_ll_ops::derive_raw_secret
.
Для ядер android11
установите BLK_CRYPTO_FEATURE_WRAPPED_KEYS
в keyslot_manager::features
, сделайте так, чтобы keyslot_mgmt_ll_ops::keyslot_program
и keyslot_mgmt_ll_ops::keyslot_evict
поддерживали программирование/изъятие аппаратно упакованных ключей, а также реализуйте keyslot_mgmt_ll_ops::derive_raw_secret
.
Тестирование
Хотя шифрование с аппаратно-обернутыми ключами сложнее тестировать, чем шифрование со стандартными ключами, его все равно можно протестировать, импортировав тестовый ключ и повторно реализовав вывод ключа, который делает оборудование. Это реализовано в vts_kernel_encryption_test
. Чтобы запустить этот тест, выполните:
atest -v vts_kernel_encryption_test
Прочитайте журнал тестирования и убедитесь, что тестовые случаи с аппаратно-обернутыми ключами (например, FBEPolicyTest.TestAesInlineCryptOptimizedHwWrappedKeyPolicy
и DmDefaultKeyTest.TestHwWrappedKey
) не были пропущены из-за того, что поддержка аппаратно-обернутых ключей не обнаружена, поскольку в этом случае результаты тестирования по-прежнему считаются «пройденными».
Включить ключи
После того, как поддержка аппаратного ключа на устройстве заработает правильно, вы можете внести следующие изменения в файл fstab
устройства, чтобы Android использовал его для FBE и шифрования метаданных:
- FBE: добавьте флаг
wrappedkey_v0
к параметруfileencryption
. Например, используйтеfileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Подробнее см. в документации FBE . - Шифрование метаданных: добавьте флаг
wrappedkey_v0
к параметруmetadata_encryption
. Например, используйтеmetadata_encryption=:wrappedkey_v0
. Более подробную информацию см. в документации по шифрованию метаданных .