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

Как и большинство программ для шифрования дисков и файлов, шифрование хранилища 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 и его непосредственного программирования в слот ключа встроенного криптографического механизма. Это позволяет шифровать/расшифровывать содержимое файлов без доступа программного обеспечения к исходному ключу. В стандартных ядрах 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 по-прежнему должен иметь возможность хранить зашифрованную версию ключей на диске, чтобы их можно было разблокировать. Для этой цели подошли бы необработанные ключи. Однако желательно, чтобы необработанные ключи вообще никогда не находились в системной памяти, чтобы их нельзя было извлечь для использования вне устройства, даже если они будут извлечены во время загрузки. По этой причине было определено понятие долговременной обертки.

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

  • Интерфейсы для генерации и импорта ключей хранилища, возвращающие их в долговременном упакованном виде. Интерфейс generate используется vold для генерации новых ключей хранилища для использования Android. Интерфейс import используется vts_kernel_encryption_test для импорта тестовых ключей.
  • Интерфейс для преобразования ключа долговременного зашифрованного хранилища в ключ временного зашифрованного хранилища. Этот интерфейс используется как vold , так и vts_kernel_encryption_test для разблокировки хранилища.

Алгоритм обертывания ключа — это деталь реализации, но он должен использовать надежный AEAD, такой как AES-256-GCM, со случайными векторами инициализации.

Требуются изменения в программном обеспечении.

В AOSP уже существует базовая структура для поддержки аппаратных ключей. Это включает поддержку в компонентах пользовательского пространства, таких как vold , а также поддержку в ядре Linux в blk-crypto , fscrypt и dm-default-key .

Изменения в ядре Linux

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

Для ядер android17 и выше:

  • Установите параметр 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 , blk_crypto_ll_ops::import_key , blk_crypto_ll_ops::generate_key и blk_crypto_ll_ops::prepare_key .

Для ядер android14 , android15 и android16 :

  • Установите параметр 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 .

Изменения в KeyMint (устаревшая версия)

В текущей версии аппаратно упакованных ключей ( wrappedkey ) генерация, импорт и подготовка аппаратно упакованных ключей используют ioctl-операторы ядра Linux BLKCRYPTOGENERATEKEY , BLKCRYPTOIMPORTKEY и BLKCRYPTOPREPAREKEY . Эти ioctl-операторы соответствуют методам в struct blk_crypto_ll_ops . Драйвер хранилища реализует эти методы и взаимодействует с аппаратным обеспечением для упаковки ключа для выполнения запрошенной операции. Для получения дополнительной информации об этих ioctl-операторах см. документацию ядра Linux.

Эти ioctl-вызовы были добавлены в Linux 6.16. На устройствах, которые не запускались с решением на основе ioctl, используется другое решение, использующее Android KeyMint (или ранее KeyMaster). Устаревшее решение ( wrappedkey_v0 ) несовместимо с основным ядром Linux или с текущим решением. Устаревшее решение использует следующие функции KeyMint:

  • Поддержка TAG_STORAGE_KEY как для генерации, так и для импорта ключей.
  • Поддержка метода convertStorageKeyToEphemeral .

Эта функциональность KeyMint необходима только на устройствах, использующих устаревшее решение, соответствующее параметру wrappedkey_v0 в файле fstab.

Устройства, использующие текущее решение, соответствующее параметру wrappedkey в файле fstab, не нуждаются в реализации этой функциональности KeyMint.

Ключи, упакованные для проверки

Хотя шифрование с аппаратными ключами сложнее протестировать, чем шифрование с необработанными ключами, всё же возможно провести тестирование, импортировав тестовый ключ и повторно реализовав алгоритм генерации ключа, используемый оборудованием. Это реализовано в 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 (или wrappedkey_v0 для устаревшей версии) к параметру fileencryption . Например, используйте fileencryption=::inlinecrypt_optimized+wrappedkey . Для получения более подробной информации см. документацию FBE .
  • Шифрование метаданных: добавьте флаг wrappedkey (или wrappedkey_v0 для устаревшей версии) к параметру metadata_encryption . Например, используйте metadata_encryption=:wrappedkey . Для получения более подробной информации см. документацию по шифрованию метаданных .

В каждом случае существует два варианта флага:

  • wrappedkey , поддерживаемый Android 17 и выше, включает текущую версию аппаратных клавиш с оберткой. Эта версия совместима с основным ядром Linux.
  • wrappedkey_v0 , поддерживаемый Android 11 и выше, включает устаревшую версию аппаратных клавиш с оберткой. Эта версия несовместима с основным ядром Linux. Она перенаправляет определенные операции через KeyMint и использует нестандартный формат на диске. Для получения дополнительной информации см. изменения KeyMint (устаревшая версия) .

На устройствах с Android 17 и выше рекомендуется использовать wrappedkey .

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