Аппаратное хранилище ключей

Наличие доверенной среды выполнения в системе на кристалле (SoC) дает возможность устройствам Android предоставлять аппаратно поддерживаемые надежные службы безопасности для ОС Android, служб платформы и даже сторонних приложений. Разработчики, которым нужны расширения для Android, должны перейти на страницу android.security.keystore .

До Android 6.0 в Android уже был простой аппаратный API-интерфейс криптосервисов, предоставляемый версиями 0.2 и 0.3 Keymaster Hardware Abstraction Layer (HAL). Хранилище ключей обеспечивало операции цифровой подписи и проверки, а также создание и импорт пар ключей асимметричной подписи. Это уже реализовано на многих устройствах, но есть много целей безопасности, которые невозможно легко достичь только с помощью API подписи. Keystore в Android 6.0 расширил Keystore API, чтобы обеспечить более широкий спектр возможностей.

В Android 6.0 в Keystore добавлены симметричные криптографические примитивы , AES и HMAC, а также система контроля доступа для ключей с аппаратной поддержкой. Элементы управления доступом задаются во время генерации ключа и применяются в течение всего срока действия ключа. Использование ключей может быть ограничено только после аутентификации пользователя и только для определенных целей или с указанными криптографическими параметрами. Дополнительные сведения см. на страницах « Теги и функции авторизации».

Помимо расширения набора криптографических примитивов, в Keystore в Android 6.0 добавлено следующее:

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

В Android 7.0 Keymaster 2 добавлена ​​поддержка аттестации ключей и привязки версий. Аттестация ключа предоставляет сертификаты открытого ключа, которые содержат подробное описание ключа и средств управления доступом к нему, чтобы сделать существование ключа в защищенном оборудовании и его конфигурацию удаленной проверкой.

Привязка версии привязывает ключи к операционной системе и версии уровня исправления. Это гарантирует, что злоумышленник, обнаруживший уязвимость в старой версии системы или программного обеспечения TEE, не сможет откатить устройство до уязвимой версии и использовать ключи, созданные в более новой версии. Кроме того, когда ключ с заданной версией и уровнем исправления используется на устройстве, которое было обновлено до более новой версии или уровня исправления, ключ обновляется до того, как его можно будет использовать, а предыдущая версия ключа становится недействительной. По мере обновления устройства ключи «двигаются вперед» вместе с устройством, но любой возврат устройства к предыдущей версии приводит к тому, что ключи становятся непригодными для использования.

В Android 8.0 Keymaster 3 перешел от уровня аппаратной абстракции (HAL) старой структуры C к интерфейсу C++ HAL, созданному на основе определения в новом языке определения аппаратного интерфейса (HIDL). В рамках изменений изменились многие типы аргументов, хотя типы и методы имеют прямое соответствие со старыми типами и методами структуры HAL. Подробнее см. на странице Функции .

В дополнение к этой версии интерфейса, Android 8.0 расширил функцию аттестации Keymaster 2 для поддержки аттестации идентификатора . Подтверждение идентификатора предоставляет ограниченный и необязательный механизм строгой проверки идентификаторов оборудования, таких как серийный номер устройства, название продукта и идентификатор телефона (IMEI/MEID). Чтобы реализовать это дополнение, Android 8.0 изменил схему аттестации ASN.1, чтобы добавить аттестацию идентификатора. Реализации Keymaster должны найти безопасный способ извлечения соответствующих элементов данных, а также определить механизм для безопасного и постоянного отключения функции.

В Android 9 обновления включали:

  • Обновление до Keymaster 4
  • Поддержка встроенных элементов безопасности
  • Поддержка безопасного импорта ключей
  • Поддержка шифрования 3DES.
  • Изменения в привязке версий, так что boot.img и system.img имеют отдельно установленные версии, чтобы обеспечить независимые обновления.

Глоссарий

Вот краткий обзор компонентов хранилища ключей и их взаимосвязей.

AndroidKeystore — это API и компонент Android Framework, используемый приложениями для доступа к функциям хранилища ключей. Он реализован как расширение стандартных API архитектуры криптографии Java и состоит из кода Java, который выполняется в собственном пространстве процесса приложения. AndroidKeystore выполняет запросы приложений на поведение хранилища ключей, перенаправляя их демону хранилища ключей.

Демон хранилища ключей — это системный демон Android, который обеспечивает доступ ко всем функциям хранилища ключей через API Binder . Он отвечает за хранение «ключевых BLOB-объектов», которые содержат фактический материал секретного ключа, зашифрованный таким образом, что Keystore может хранить их, но не использовать или раскрывать.

keymasterd — это HIDL-сервер, предоставляющий доступ к Keymaster TA. (Это имя не стандартизировано и предназначено для концептуальных целей.)

Keymaster TA (доверенное приложение) — это программное обеспечение, работающее в безопасном контексте, чаще всего в TrustZone на SoC ARM, которое обеспечивает все безопасные операции с хранилищем ключей, имеет доступ к исходному ключевому материалу, проверяет все условия контроля доступа к ключам. , так далее.

LockSettingsService — системный компонент Android, отвечающий за аутентификацию пользователя по паролю и отпечатку пальца. Это не часть хранилища ключей, но актуально, поскольку многие операции с ключами в хранилище ключей требуют аутентификации пользователя. LockSettingsService взаимодействует с TA Gatekeeper и TA Fingerprint для получения токенов аутентификации, которые он предоставляет демону хранилища ключей и которые в конечном итоге используются приложением Keymaster TA.

Gatekeeper TA (доверенное приложение) — это еще один компонент, работающий в безопасном контексте, который отвечает за аутентификацию паролей пользователей и создание маркеров аутентификации, используемых для подтверждения Keymaster TA, что аутентификация была выполнена для конкретного пользователя в определенный момент времени.

TA отпечатков пальцев (доверенное приложение) — это еще один компонент, работающий в защищенном контексте, который отвечает за аутентификацию отпечатков пальцев пользователя и создание токенов аутентификации, используемых для подтверждения TA Keymaster, что аутентификация была выполнена для конкретного пользователя в определенный момент времени.

Архитектура

Android Keystore API и лежащий в его основе Keymaster HAL предоставляют базовый, но достаточный набор криптографических примитивов, позволяющий реализовывать протоколы с использованием аппаратных ключей с контролируемым доступом.

Keymaster HAL — это динамически загружаемая библиотека OEM, используемая службой хранилища ключей для предоставления аппаратных криптографических услуг. Чтобы обеспечить безопасность, реализации HAL не выполняют никаких конфиденциальных операций в пространстве пользователя или даже в пространстве ядра. Конфиденциальные операции делегируются защищенному процессору, доступ к которому осуществляется через некоторый интерфейс ядра. Результирующая архитектура выглядит следующим образом:

Доступ к мастеру ключей

Рис. 1. Доступ к Keymaster

В устройстве Android «клиент» Keymaster HAL состоит из нескольких уровней (например, приложение, инфраструктура, демон хранилища ключей), но это можно игнорировать для целей данного документа. Это означает, что описанный Keymaster HAL API является низкоуровневым, используется внутренними компонентами платформы и не предоставляется разработчикам приложений. API более высокого уровня описан на сайте Android Developer .

Назначение Keymaster HAL состоит не в том, чтобы реализовать алгоритмы, чувствительные к безопасности, а только в том, чтобы маршалировать и демаршалировать запросы к безопасному миру. Формат проводника определяется реализацией.

Совместимость с предыдущими версиями

HAL Keymaster 1 полностью несовместим с ранее выпущенными HAL, например, Keymaster 0.2 и 0.3. Для облегчения взаимодействия на устройствах под управлением Android 5.0 и более ранних версий, запущенных со старыми HAL Keymaster, Keystore предоставляет адаптер, который реализует HAL Keymaster 1 с вызовами существующей аппаратной библиотеки. Результат не может обеспечить полный набор функций HAL Keymaster 1. В частности, он поддерживает только алгоритмы RSA и ECDSA, а принудительная авторизация всех ключей выполняется адаптером в незащищенном мире.

Keymaster 2 еще больше упростил интерфейс HAL, удалив методы get_supported_* и позволив методу finish() принимать ввод. Это уменьшает количество обращений к TEE в тех случаях, когда все входные данные доступны сразу, и упрощает реализацию расшифровки AEAD.

В Android 8.0 Keymaster 3 перешел от старого стиля HAL C-структуры к интерфейсу C++ HAL, сгенерированному из определения на новом языке определения аппаратного интерфейса (HIDL). Реализация HAL в новом стиле создается путем создания подкласса сгенерированного класса IKeymasterDevice и реализации чисто виртуальных методов. В рамках изменений изменились многие типы аргументов, хотя типы и методы имеют прямое соответствие со старыми типами и методами структуры HAL.

Обзор HIDL

Язык определения аппаратных интерфейсов (HIDL) предоставляет независимый от языка реализации механизм для определения аппаратных интерфейсов. Инструментарий HIDL в настоящее время поддерживает создание интерфейсов C++ и Java. Ожидается, что большинство разработчиков Trusted Execution Environment (TEE) сочтут инструменты C++ более удобными, поэтому в этом документе обсуждается только представление C++.

Интерфейсы HIDL состоят из набора методов, выраженных как:

  methodName(INPUT ARGUMENTS) generates (RESULT ARGUMENTS);

Существуют различные предопределенные типы, и HAL могут определять новые перечисляемые и структурные типы. Дополнительные сведения о HIDL см. в разделе « Справочник» .

Пример метода из Keymaster 3 IKeymasterDevice.hal :

generateKey(vec<KeyParameter> keyParams)
        generates(ErrorCode error, vec<uint8_t> keyBlob,
                  KeyCharacteristics keyCharacteristics);

Это эквивалентно следующему из keymaster2 HAL:

keymaster_error_t (*generate_key)(
        const struct keymaster2_device* dev,
        const keymaster_key_param_set_t* params,
        keymaster_key_blob_t* key_blob,
        keymaster_key_characteristics_t* characteristics);

В версии HIDL аргумент dev удален, поскольку он неявный. Аргумент params больше не является структурой, содержащей указатель, ссылающийся на массив объектов key_parameter_t , а представляет собой vec (вектор), содержащий объекты KeyParameter . Возвращаемые значения перечислены в предложении " generates ", включая вектор значений uint8_t для ключевого BLOB-объекта.

Виртуальный метод C++, созданный компилятором HIDL:

Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                         generateKey_cb _hidl_cb) override;

Где generateKey_cb — это указатель на функцию, определяемый как:

std::function<void(ErrorCode error, const hidl_vec<uint8_t>& keyBlob,
                   const KeyCharacteristics& keyCharacteristics)>

То есть generateKey_cb — это функция, которая принимает возвращаемые значения, перечисленные в предложении generate. Класс реализации HAL переопределяет этот метод generateKey и вызывает указатель функции generateKey_cb , чтобы вернуть результат операции вызывающей стороне. Обратите внимание, что вызов указателя функции является синхронным . Вызывающий вызывает generateKey а generateKey вызывает предоставленный указатель функции, который выполняется до завершения, возвращая управление реализации generateKey , которая затем возвращается вызывающему.

Подробный пример см. в описании реализации по умолчанию в hardware/interfaces/keymaster/3.0/default/KeymasterDevice.cpp . Реализация по умолчанию обеспечивает обратную совместимость для устройств со старым стилем keymaster0, keymaster1 или keymaster2 HALS.

Контроль доступа

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

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

Домены хранилища ключей

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

Мы вводим пять параметров домена, которые управляют доступом к ключам. Они управляют семантикой параметра пространства имен дескриптора ключа и тем, как выполняется управление доступом.

  • DOMAIN_APP : домен приложения охватывает устаревшее поведение. SPI хранилища ключей Java использует этот домен по умолчанию. Когда используется этот домен, аргумент пространства имен игнорируется и вместо него используется UID вызывающей стороны. Доступ к этому домену контролируется меткой Keystore для класса keystore_key в политике SELinux.
  • DOMAIN_SELINUX : этот домен указывает, что пространство имен имеет метку в политике SELinux. Параметр пространства имен просматривается и транслируется в целевой контекст, а также выполняется проверка разрешений для вызывающего контекста SELinux для класса keystore_key . Когда для данной операции установлено разрешение, для поиска ключа используется полный кортеж.
  • DOMAIN_GRANT : домен предоставления указывает, что параметр пространства имен является идентификатором предоставления. Параметр псевдонима игнорируется. Проверки SELinux выполняются при создании гранта. Дальнейшее управление доступом только проверяет, соответствует ли UID вызывающего абонента UID получателя запрошенного гранта.
  • DOMAIN_KEY_ID : этот домен указывает, что параметр пространства имен является уникальным идентификатором ключа. Сам ключ мог быть создан с помощью DOMAIN_APP или DOMAIN_SELINUX . Проверка разрешений выполняется после загрузки domain и namespace из базы данных ключей так же, как если бы большой двоичный объект был загружен кортежем домена, пространства имен и псевдонима. Обоснованием ключевого домена id является непрерывность. При доступе к ключу по псевдониму последующие вызовы могут работать с другими ключами, поскольку новый ключ мог быть сгенерирован или импортирован и привязан к этому псевдониму. Однако идентификатор ключа никогда не меняется. Таким образом, при использовании ключа по идентификатору ключа после того, как он был загружен из базы данных хранилища ключей с использованием псевдонима один раз, можно быть уверенным, что это тот же ключ, пока идентификатор ключа все еще существует. Эта функциональность недоступна разработчикам приложений. Вместо этого он используется в SPI хранилища ключей Android для обеспечения более согласованной работы даже при одновременном небезопасном использовании.
  • DOMAIN_BLOB : домен большого двоичного объекта указывает, что вызывающий объект сам управляет большим двоичным объектом. Это используется для клиентов, которым необходимо получить доступ к хранилищу ключей до монтирования раздела данных. Ключевой большой blob объект включается в поле большого двоичного объекта дескриптора ключа.

Используя домен SELinux, мы можем предоставить компонентам поставщика доступ к очень конкретным пространствам имен хранилища ключей, которые могут совместно использоваться системными компонентами, такими как диалоговое окно настроек.

Политика SELinux для keystore_key

Метки пространства имен настраиваются с помощью файла keystore2_key_context .
Каждая строка в этих файлах сопоставляет числовой идентификатор пространства имен с меткой SELinux. Например,

# wifi_key is a keystore2_key namespace intended to be used by wpa supplicant and
# Settings to share keystore keys.
102            u:object_r:wifi_key:s0

Настроив таким образом новое пространство имен ключей, мы можем предоставить к нему доступ, добавив соответствующую политику. Например, чтобы позволить wpa_supplicant получать и использовать ключи в новом пространстве имен, мы должны добавить следующую строку в hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

После настройки нового пространства имен AndroidKeyStore можно использовать почти как обычно. Единственное отличие состоит в том, что должен быть указан идентификатор пространства имен. Для загрузки и импорта ключей из хранилища ключей и в него идентификатор пространства имен указывается с помощью параметра AndroidKeyStoreLoadStoreParameter . Например,

import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import java.security.KeyStore;

KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(new AndroidKeyStoreLoadStoreParameter(102));

Чтобы сгенерировать ключ в заданном пространстве имен, идентификатор пространства имен должен быть задан с помощью KeyGenParameterSpec.Builder#setNamespace():

import android.security.keystore.KeyGenParameterSpec;
KeyGenParameterSpec.Builder specBuilder = new KeyGenParameterSpec.Builder();
specBuilder.setNamespace(102);

Следующие файлы контекста могут использоваться для настройки пространств имен SELinux хранилища ключей 2.0. Каждый раздел имеет свой зарезервированный диапазон из 10 000 идентификаторов пространства имен, чтобы избежать конфликтов.

Раздел Диапазон Файлы конфигурации
Система 0 ... 9 999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Расширенная система 10 000 ... 19 999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Товар 20 000 ... 29 999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Продавец 30 000 ... 39 999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

Клиент запрашивает ключ, запрашивая домен SELinux и желаемое виртуальное пространство имен, в данном случае "wifi_key" , по его числовому идентификатору.

Кроме того, были определены следующие пространства имен. Если они заменяют специальные правила, в следующей таблице указывается UID, которому они раньше соответствовали.

Идентификатор пространства имен Метка SEPolicy UID Описание
0 su_key Н/Д Ключ суперпользователя. Используется только для тестирования на сборках userdebug и eng. Не имеет значения для пользовательских сборок.
1 shell_key Н/Д Пространство имен, доступное для оболочки. В основном используется для тестирования, но может использоваться и в пользовательских сборках из командной строки.
100 vold_key Н/Д Предназначен для использования vold.
101 odsing_key Н/Д Используется демоном подписи на устройстве.
102 wifi_key AID_WIFI(1010) Используется подсистемой Android Wifi, включая wpa_supplicant.
120 резюме_на_перезагрузке_ключ AID_SYSTEM(1000) Используется системным сервером Android для поддержки возобновления работы при перезагрузке.

Векторы доступа

Класс keystore_key в SELinux сильно устарел, и некоторые разрешения, такие как verify или sign , потеряли свое значение. Вот новый набор разрешений, keystore2_key , который будет применяться в Keystore 2.0.

Разрешение Значение
delete Проверяется при удалении ключей из Keystore.
get_info Проверяется, когда запрашиваются метаданные ключа.
grant Вызывающему объекту необходимо это разрешение для создания гранта для ключа в целевом контексте.
manage_blob Вызывающий объект может использовать DOMAIN_BLOB в заданном пространстве имен SELinux, тем самым самостоятельно управляя большими двоичными объектами. Это особенно полезно для vold.
rebind Это разрешение определяет, может ли псевдоним быть повторно привязан к новому ключу. Это необходимо для вставки и означает, что ранее привязанный ключ будет удален. По сути, это разрешение на вставку, но оно лучше отражает семантику хранилища ключей.
req_forced_op Клиенты с этим разрешением могут создавать операции, которые нельзя удалить, и создание операции никогда не завершается ошибкой, если только все слоты операций не заняты операциями, которые нельзя удалить.
update Требуется для обновления подкомпонента ключа.
use Проверяется при создании операции Keymint, которая использует материал ключа, например, для подписи, шифрования/дешифрования.
use_dev_id Требуется при создании информации, идентифицирующей устройство, например аттестации идентификатора устройства.

Кроме того, мы выделили набор разрешений хранилища ключей, не относящихся к ключу, в классе безопасности SELinux keystore2 :

Разрешение Значение
add_auth Требуется поставщику аутентификации, например Gatekeeper или BiometricsManager, для добавления токенов аутентификации.
clear_ns Это разрешение, ранее называвшееся clear_uid, позволяет пользователю, не являющемуся владельцем пространства имен, удалять все ключи в этом пространстве имен.
list Требуется системе для перечисления ключей по различным свойствам, таким как право собственности или ограниченность аутентификации. Это разрешение не требуется для вызывающих объектов, перечисляющих свои собственные пространства имен. На это распространяется разрешение get_info .
lock Это разрешение позволяет заблокировать хранилище ключей, то есть удалить мастер-ключ, чтобы ключи, привязанные к аутентификации, стали непригодными для использования и создания.
reset Это разрешение позволяет сбросить хранилище ключей до заводских значений по умолчанию, удалив все ключи, которые не являются жизненно важными для функционирования ОС Android.
unlock Это разрешение требуется, чтобы попытаться разблокировать главный ключ для связанных ключей аутентификации.