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

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

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

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

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

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

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

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

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

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

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

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

Глоссарий

Вот краткий обзор компонентов Keystore и их взаимосвязей.

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

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

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

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

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

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

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

Архитектура

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

Keymaster HAL — это динамически загружаемая библиотека, предоставляемая OEM-производителем и используемая службой Keystore для предоставления криптографических услуг с аппаратной поддержкой. Чтобы обеспечить безопасность, реализации 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 с вызовами к существующей аппаратной библиотеке. Результат не может обеспечить полный набор функций Keymaster 1 HAL. В частности, он поддерживает только алгоритмы RSA и ECDSA, а вся проверка авторизации ключей выполняется адаптером в незащищенном мире.

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

В Android 8.0 Keymaster 3 перешёл от старой C-структуры HAL к интерфейсу HAL C++, созданному на основе определения в новом языке определения аппаратного интерфейса (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);

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

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 для ключевого объекта.

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

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

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

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

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

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

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

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

  • DOMAIN_APP : Домен приложения охватывает устаревшее поведение. SPI хранилища ключей Java использует этот домен по умолчанию. При использовании этого домена аргумент пространства имен игнорируется и вместо него используется UID вызывающего объекта. Доступ к этому домену контролируется меткой Keystore для класса keystore_key в политике SELinux.
  • DOMAIN_SELINUX : этот домен указывает, что пространство имен имеет метку в политике SELinux. Параметр пространства имен ищется и преобразуется в целевой контекст, а также выполняется проверка разрешений для вызывающего контекста SELinux для класса keystore_key . Когда для данной операции установлено разрешение, для поиска ключа используется полный кортеж.
  • DOMAIN_GRANT : Домен предоставления указывает, что параметр пространства имен является идентификатором разрешения. Параметр alias игнорируется. Проверки SELinux выполняются при создании гранта. Дальнейший контроль доступа только проверяет, соответствует ли UID вызывающего абонента UID получателя запрошенного гранта.
  • DOMAIN_KEY_ID : этот домен указывает, что параметр пространства имен является уникальным идентификатором ключа. Сам ключ мог быть создан с помощью DOMAIN_APP или DOMAIN_SELINUX . Проверка разрешений выполняется после загрузки domain и namespace из базы данных ключей так же, как если бы большой двоичный объект был загружен доменом, пространством имен и кортежем псевдонимов. Обоснованием домена key id является непрерывность. При доступе к ключу по псевдониму последующие вызовы могут работать с другими ключами, поскольку новый ключ мог быть сгенерирован или импортирован и привязан к этому псевдониму. Однако идентификатор ключа никогда не меняется. Таким образом, при использовании ключа по идентификатору ключа после его однократной загрузки из базы данных Keystore с использованием псевдонима можно быть уверенным, что это тот же самый ключ, пока идентификатор ключа все еще существует. Эта функция недоступна разработчикам приложений. Вместо этого он используется в Android Keystore SPI для обеспечения более единообразной работы даже при одновременном использовании небезопасным способом.
  • 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 Keystore 2.0. Каждый раздел имеет отдельный зарезервированный диапазон из 10 000 идентификаторов пространства имен, чтобы избежать конфликтов.

Раздел Диапазон Конфигурационные файлы
Система 0...9999
/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 шелл_ключ Н/Д Пространство имен, доступное оболочке. В основном используется для тестирования, но может использоваться и в пользовательских сборках из командной строки.
100 vold_key Н/Д Предназначен для использования Vold.
101 odsing_key Н/Д Используется демоном подписи на устройстве.
102 wifi_key АИД_WIFI(1010) Используется системной системой Wi-Fi Android, включая wpa_supplicant.
120 резюме_on_reboot_key АИД_СИСТЕМ (1000) Используется системным сервером Android для поддержки возобновления работы при перезагрузке.

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

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

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