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

Как и большинство программ для шифрования дисков и файлов, шифрование хранилища 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, отвечающий требованиям безопасности. Однако в целях тестирования необходимо повторно реализовать тот же KDF в тестовом коде. В настоящее время один KDF рассмотрен и внедрен; его можно найти в исходном коде vts_kernel_encryption_test . Рекомендуется, чтобы аппаратное обеспечение использовало этот KDF, который использует NIST SP 800-108 «KDF в режиме счетчика» с AES-256-CMAC в качестве PRF. Обратите внимание: для совместимости все части алгоритма должны быть идентичными, включая выбор контекстов KDF и меток для каждого подраздела.

Упаковка ключей

Для достижения целей безопасности аппаратных ключей определены два типа упаковки ключей:

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

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

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

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

  • Интерфейсы для создания и импорта ключей хранилища, возвращающие их в долговременной упаковке. Доступ к этим интерфейсам осуществляется косвенно через KeyMint, и они соответствуют тегу TAG_STORAGE_KEY KeyMint. Возможность «генерации» используется vold для создания новых ключей хранения для использования Android, а возможность «импорта» используется 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 .

keyslot_mgmt_ll_ops::keyslot_program android11 keyslot_mgmt_ll_ops::derive_raw_secret установка BLK_CRYPTO_FEATURE_WRAPPED_KEYS keyslot_mgmt_ll_ops::keyslot_evict keyslot_manager::features

Тестирование

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