Как и большинство программ для шифрования дисков и файлов, шифрование хранилища Android традиционно требует наличия необработанных ключей шифрования в системной памяти для выполнения шифрования. Даже если шифрование выполняется аппаратно, а не программно, программному обеспечению, как правило, всё равно необходимо управлять необработанными ключами шифрования.
Традиционно это не считается проблемой, поскольку ключи отсутствуют во время офлайн-атаки, которая является основным типом атак, от которых призвано защищать шифрование хранилища. Однако существует потребность в усилении защиты от других типов атак, таких как атаки с холодной загрузкой и онлайн-атаки, при которых злоумышленник может получить утечку системной памяти, не подвергая устройство полному риску.
Для решения этой проблемы в Android 11 появилась поддержка аппаратно-защищённых ключей (если таковая имеется). Аппаратно-защищённые ключи — это ключи хранения, которые известны в необработанном виде только специализированному оборудованию; программное обеспечение видит эти ключи и работает с ними только в зашифрованном виде. Это оборудование должно иметь возможность генерировать и импортировать ключи хранения, защищать ключи хранения в эфемерной и долгосрочной формах, создавать подключаемые ключи, напрямую программировать один подключаемый ключ во встроенный криптографический механизм и возвращать отдельный подключаемый ключ программному обеспечению.
Примечание: Встроенный криптографический модуль (или встроенное криптографическое оборудование ) — это оборудование, которое шифрует/дешифрует данные при их передаче на устройство хранения или с него. Обычно это хост-контроллер UFS или eMMC, реализующий криптографические расширения, определённые соответствующей спецификацией JEDEC.
Дизайн
В этом разделе представлена структура функции аппаратной защиты ключей, включая необходимую для неё аппаратную поддержку. Основное внимание уделяется файловому шифрованию (FBE), но данное решение применимо и к шифрованию метаданных .
Один из способов избежать необходимости хранить необработанные ключи шифрования в системной памяти — хранить их только в слотах ключей встроенного криптографического движка. Однако такой подход сопряжен с рядом проблем:
- Количество ключей шифрования может превышать количество слотов ключей.
- Встроенные криптографические модули обычно теряют содержимое своих слотов ключей при сбросе хост-контроллера хранилища. Сброс хост-контроллера хранилища — это стандартная процедура восстановления после ошибок, которая выполняется при возникновении определённых типов ошибок хранилища, и такие ошибки могут возникнуть в любой момент. Поэтому при использовании встроенного шифрования операционная система всегда должна быть готова к перепрограммированию слотов ключей без вмешательства пользователя.
- Встроенные криптографические механизмы могут использоваться только для шифрования/дешифрования целых блоков данных на диске. Однако в случае FBE программное обеспечение по-прежнему должно уметь выполнять другие криптографические операции, такие как шифрование имён файлов и получение идентификаторов ключей. Для выполнения этих операций программному обеспечению по-прежнему потребуется доступ к необработанным ключам FBE.
Чтобы избежать этих проблем, ключи хранилища преобразуются в аппаратно защищённые ключи , которые могут быть распакованы и использованы только специализированным оборудованием. Это позволяет поддерживать неограниченное количество ключей. Кроме того, иерархия ключей изменяется и частично переносится на это оборудование, что позволяет возвращать подключаемый ключ программному обеспечению для задач, не использующих встроенный криптографический механизм.
Ключевая иерархия
Ключи могут быть получены из других ключей с использованием функции вывода ключей (KDF), такой как HKDF , что приводит к иерархии ключей .
На следующей диаграмме изображена типичная иерархия ключей для FBE, когда аппаратно защищенные ключи не используются:
Ключ класса FBE — это необработанный ключ шифрования, который Android передаёт ядру Linux для разблокировки определённого набора зашифрованных каталогов, например, хранилища, зашифрованного учётными данными конкретного пользователя Android. (В ядре этот ключ называется главным ключом fscrypt .) Из этого ключа ядро выводит следующие подключи:
- Идентификатор ключа. Он не используется для шифрования, а представляет собой значение, используемое для идентификации ключа, которым защищён конкретный файл или каталог.
- Ключ шифрования содержимого файла
- Ключ шифрования имен файлов
Напротив, следующая диаграмма отображает иерархию ключей для FBE при использовании аппаратно защищенных ключей:
По сравнению с предыдущим случаем, в иерархию ключей был добавлен дополнительный уровень, а ключ шифрования содержимого файла был перемещён. Корневой узел по-прежнему представляет собой ключ, который Android передаёт Linux для разблокировки набора зашифрованных каталогов. Однако теперь этот ключ находится в эфемерно завёрнутом виде, и для его использования необходимо передать его на специализированное оборудование. Это оборудование должно реализовать два интерфейса, принимающих эфемерно завёрнутый ключ:
- Один интерфейс для получения
inline_encryption_key
и его непосредственного программирования в слот ключей встроенного криптографического движка. Это позволяет шифровать/расшифровывать содержимое файлов без доступа программного обеспечения к исходному ключу. В стандартных ядрах 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, отвечающий требованиям безопасности. Однако в целях тестирования vts_kernel_encryption_test
реализует тот же KDF программно для воспроизведения зашифрованного текста на диске и проверки его корректности. Для простоты тестирования и использования безопасного и проверенного KDF мы рекомендуем реализовывать на оборудовании KDF по умолчанию, который проверяется тестом. Если на оборудовании используется другой 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
) не были пропущены из-за отсутствия поддержки аппаратно-обернутых ключей, поскольку в этом случае результаты тестирования все равно будут «пройдены».
По умолчанию vts_kernel_encryption_test
предполагает, что аппаратное обеспечение реализует функцию криптографического шифрования (KDF), называемую kdf1
. Эта функция относится к семейству функций криптографического шифрования (KDF) счётного режима из спецификации NIST SP 800-108 и использует AES-256-CMAC в качестве псевдослучайной функции. Подробнее о CMAC см. в спецификации CMAC . Функция криптографического шифрования использует определённые контексты и метки при формировании каждого подключаемого ключа. Аппаратное обеспечение должно реализовать эту функцию криптографического шифрования (KDF), включая точный выбор контекста, метки и форматирования фиксированной входной строки при формировании каждого подключаемого ключа.
Однако vts_kernel_encryption_test
также реализует дополнительные KDF-функции kdf2
– kdf4
. Они столь же безопасны, как и kdf1
, и отличаются только выбором контекстов, меток и форматированием фиксированной входной строки. Они существуют только для адаптации к разному оборудованию.
Для устройств, использующих другой KDF, задайте системному свойству ro.crypto.hw_wrapped_keys.kdf
в PRODUCT_VENDOR_PROPERTIES
имя KDF, как определено в исходном коде теста. Это заставит vts_kernel_encryption_test
проверять этот KDF вместо kdf1
. Например, чтобы выбрать kdf2
, используйте:
PRODUCT_VENDOR_PROPERTIES += ro.crypto.hw_wrapped_keys.kdf=kdf2
Для устройств, использующих KDF, который тест не поддерживает, также добавьте реализацию этого KDF в тест и дайте ему уникальное имя.
Включить завернутые ключи
Если поддержка аппаратного ключа на устройстве работает правильно, внесите следующие изменения в файл fstab
устройства, чтобы Android использовал его для FBE и шифрования метаданных:
- FBE: добавьте флаг
wrappedkey_v0
к параметруfileencryption
. Например, используйтеfileencryption=::inlinecrypt_optimized+wrappedkey_v0
. Подробнее см. в документации FBE . - Шифрование метаданных: добавьте флаг
wrappedkey_v0
к параметруmetadata_encryption
. Например, используйтеmetadata_encryption=:wrappedkey_v0
. Подробнее см. в документации по шифрованию метаданных .