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

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

Наличие доверенной среды выполнения в системе на кристалле (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. Параметр namespace просматривается и транслируется в целевой контекст, а также выполняется проверка разрешений для вызывающего контекста SELinux для класса keystore_key . Когда для данной операции установлено разрешение, для поиска ключа используется полный кортеж.
  • DOMAIN_GRANT : домен предоставления указывает, что параметр пространства имен является идентификатором предоставления. The alias parameter is ignored. SELinux checks are performed when the grant is created. Further access control only checks if the caller UID matches the grantees UID of the requested grant.
  • DOMAIN_KEY_ID : This domain indicates that the namespace parameter is a unique key id. The key itself may have been created with DOMAIN_APP or DOMAIN_SELINUX . The permission check is performed after the domain and the namespace have been loaded from the key database in the same way as if the blob was loaded by the domain, namespace, and alias tuple. The rationale for the key id domain is continuity. When accessing a key by alias, subsequent calls may operate on different keys, because a new key may have been generated or imported and bound to this alias. The key id, however, never changes. So when using a key by key id after it has been loaded from the Keystore database using the alias once, one can be certain that it is the same key as long as the key id still exists. This functionality is not exposed to app developers. Instead, it is used within the Android Keystore SPI to provide a more consistent experience even when used concurrently in an unsafe way.
  • DOMAIN_BLOB : The blob domain indicates that the caller manages the blob by itself. This is used for clients that need to access the Keystore before the data partition is mounted. The key blob is included in the blob field of the key descriptor.

Using the SELinux domain, we can give vendor components access to very specific Keystore namespaces which can be shared by system components such as the settings dialog.

SELinux policy for keystore_key

Namespace labels are configured using the keystore2_key_context file.
Each line in these files maps a numeric namespace id to an SELinux label. For example,

# 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

After having set up a new key namespace in this way, we can give access to it by adding an appropriate policy. For example, to allow wpa_supplicant to get and use keys in the new namespace we would add the following line to hal_wifi_supplicant.te :

allow hal_wifi_supplicant wifi_key:keystore2_key { get, use };

After setting up the new namespace, AndroidKeyStore can be used almost as usual. The only difference is that the namespace ID must be specified. For loading and importing keys from and into Keystore, the namespace id is specified using the AndroidKeyStoreLoadStoreParameter . For example,

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

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

To generate a key in a given namespace, the namespace id must be given using KeyGenParameterSpec.Builder#setNamespace():

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

The following context files may be used to configure Keystore 2.0 SELinux namespaces. Each partition has a different reserved range of 10,000 namespace ids to avoid collisions.

Partition Range Config files
System 0 ... 9,999
/system/etc/selinux/keystore2_key_contexts, /plat_keystore2_key_contexts
Extended System 10,000 ... 19,999
/system_ext/etc/selinux/system_ext_keystore2_key_contexts, /system_ext_keystore2_key_contexts
Product 20,000 ... 29,999
/product/etc/selinux/product_keystore2_key_contexts, /product_keystore2_key_contexts
Vendor 30,000 ... 39,999
/vendor/etc/selinux/vendor_keystore2_key_contexts, /vendor_keystore2_key_contexts

The client requests the key by requesting the SELinux domain and the desired virtual namespace, in this case "wifi_key" , by its numeric id.

Above that, the following namespaces have been defined. If they replace special rules, the following table indicates the UID they used to correspond to.

Namespace ID SEPolicy Label UID Описание
0 su_key Н/Д Super user key. Only used for testing on userdebug and eng builds. Not relevant on user builds.
1 shell_key Н/Д Namespace available to shell. Mostly used for testing, but can be used on user builds as well from the command line.
100 vold_key Н/Д Intended for use by vold.
101 odsing_key Н/Д Used by the on-device signing daemon.
102 wifi_key AID_WIFI(1010) Used by Android's Wifi sybsystem including wpa_supplicant.
120 resume_on_reboot_key AID_SYSTEM(1000) Used by Android's system server to support resume on reboot.

Access Vectors

The SELinux class keystore_key has aged quite a bit and some of the permissions, such as verify or sign have lost their meaning. Here is the new set of permissions, keystore2_key , that Keystore 2.0 will enforce.

Permission Meaning
delete Checked when removing keys from Keystore.
get_info Checked when a key's metadata is requested.
grant The caller needs this permission to create a grant to the key in the target context.
manage_blob The caller may use DOMAIN_BLOB on the given SELinux namespace, thereby managing blobs by itself. This is specifically useful for vold.
rebind This permission controls if an alias may be rebound to a new key. This is required for insertion and implies that the previously bound key will be deleted. It is basically an insert permission, but it captures the semantic of keystore better.
req_forced_op Clients with this permission may create unpruneable operations, and operation creation never fails unless all operation slots are taken by unpruneable operations.
update Required to update the subcomponent of a key.
use Checked when creating a Keymint operation that uses the key material, eg, for signing, en/decryption.
use_dev_id Required when generating device identifying information, such as device id attestation.

Additionally, we split out a set of non key specific keystore permissions in the SELinux security class keystore2 :

Permission Meaning
add_auth Required by authentication provider such as Gatekeeper or BiometricsManager for adding auth tokens.
clear_ns Formerly clear_uid, this permission allows a non owner of a namespace to delete all keys in that namespace.
list Required by the system for enumerating keys by various properties, such as ownership or auth boundedness. This permission is not required by callers enumerating their own namespaces. This is covered by the get_info permission.
lock This permission allows to lock Keystore, that is, evict the master key, such that auth bound keys become unusable and uncreatable.
reset This permission allows to reset Keystore to factory default, deleting all keys that are not vital to the functioning of the Android OS.
unlock This permission is required to attempt to unlock the master key for auth bound keys.