Аппаратные ключи

Как и большинство программ для шифрования дисков и файлов, шифрование хранилища Android традиционно полагается на наличие необработанных ключей шифрования в системной памяти, чтобы можно было выполнить шифрование. Даже когда шифрование выполняется выделенным оборудованием, а не программным обеспечением, программному обеспечению обычно все равно необходимо управлять необработанными ключами шифрования.

Это традиционно не рассматривается как проблема, поскольку ключи отсутствуют во время офлайн-атаки, которая является основным типом атаки, от которой призвано защищать шифрование хранилища. Однако есть желание обеспечить повышенную защиту от других типов атак, таких как атаки с холодной загрузкой и онлайн-атаки, когда злоумышленник может получить утечку системной памяти без полной компрометации устройства.

Чтобы решить эту проблему, Android 11 представила поддержку аппаратно-обернутых ключей , где присутствует аппаратная поддержка. Аппаратно-обернутые ключи — это ключи хранения, которые известны в необработанном виде только выделенному оборудованию; программное обеспечение видит и работает с этими ключами только в обернутой (зашифрованной) форме. Это оборудование должно иметь возможность генерировать и импортировать ключи хранения, оборачивать ключи хранения в эфемерные и долгосрочные формы, выводить подключи, напрямую программировать один подключ во встроенный криптографический движок и возвращать отдельный подключ программному обеспечению.

Примечание: Встроенный криптодвижок (или встроенное криптооборудование ) относится к оборудованию, которое шифрует/дешифрует данные, пока они находятся на пути к/от устройства хранения. Обычно это хост-контроллер UFS или eMMC, который реализует крипторасширения, определенные соответствующей спецификацией JEDEC.

Дизайн

В этом разделе представлена ​​конструкция функции аппаратно-обернутых ключей, включая то, какая аппаратная поддержка требуется для нее. Это обсуждение фокусируется на файловом шифровании (FBE), но решение применимо и к шифрованию метаданных .

Один из способов избежать необходимости в необработанных ключах шифрования в системной памяти — хранить их только в слотах ключей встроенного криптодвижка. Однако этот подход сталкивается с некоторыми проблемами:

  • Количество ключей шифрования может превышать количество слотов ключей.
  • Встроенные криптодвижки обычно теряют содержимое своих слотов ключей, если контроллер хоста хранилища сбрасывается. Сброс контроллера хоста хранилища — это стандартная процедура восстановления после ошибок, которая выполняется при возникновении определенных типов ошибок хранилища, и такие ошибки могут возникнуть в любое время. Поэтому при использовании встроенного шифрования операционная система всегда должна быть готова перепрограммировать слоты ключей без вмешательства пользователя.
  • Встроенные криптодвижки могут использоваться только для шифрования/дешифрования полных блоков данных на диске. Однако в случае FBE программное обеспечение все равно должно уметь выполнять другую криптографическую работу, такую ​​как шифрование имен файлов и получение идентификаторов ключей. Программному обеспечению все равно потребуется доступ к необработанным ключам FBE для выполнения этой другой работы.

Чтобы избежать этих проблем, ключи хранения вместо этого превращаются в аппаратно-обернутые ключи , которые могут быть распакованы и использованы только выделенным оборудованием. Это позволяет поддерживать неограниченное количество ключей. Кроме того, иерархия ключей изменяется и частично перемещается на это оборудование, что позволяет возвращать подключ в программное обеспечение для задач, которые не могут использовать встроенный криптодвижок.

Иерархия ключей

Ключи могут быть получены из других ключей с помощью функции вывода ключей (KDF) , такой как HKDF , что приводит к иерархии ключей .

На следующей диаграмме изображена типичная иерархия ключей для FBE, когда аппаратно защищенные ключи не используются:

Иерархия ключей FBE (стандартная)
Рисунок 1. Иерархия ключей FBE (стандартная)

Ключ класса FBE — это необработанный ключ шифрования, который Android передает ядру Linux для разблокировки определенного набора зашифрованных каталогов, например, хранилища с зашифрованными учетными данными для определенного пользователя Android. (В ядре этот ключ называется главным ключом fscrypt .) Из этого ключа ядро ​​выводит следующие подключи:

  • Идентификатор ключа. Он не используется для шифрования, а скорее является значением, используемым для идентификации ключа, с помощью которого защищен определенный файл или каталог.
  • Ключ шифрования содержимого файла
  • Ключ шифрования имен файлов

Напротив, следующая диаграмма отображает иерархию ключей для FBE при использовании аппаратно защищенных ключей:

Иерархия ключей FBE (с аппаратно запечатанным ключом)
Рисунок 2. Иерархия ключей 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, который соответствует требованиям безопасности. Однако в целях тестирования 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 принадлежит к семейству KDF с режимом счетчика из NIST SP 800-108 и использует AES-256-CMAC в качестве псевдослучайной функции. Для получения дополнительной информации о CMAC см. спецификацию CMAC . KDF использует определенные контексты и метки при выводе каждого подключаемого ключа. Оборудование должно реализовать этот 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 . Более подробную информацию см. в документации по шифрованию метаданных .