Архитектура подписи на устройстве

Начиная с Android 12, модуль Android Runtime (ART) является основным модулем. Обновление модуля может потребовать пересборки артефактов предварительной компиляции (AOT) jar-файлов bootclasspath и системного сервера. Поскольку эти артефакты важны с точки зрения безопасности, в Android 12 используется функция подписи на устройстве, предотвращающая их несанкционированное изменение. На этой странице рассматривается архитектура подписи на устройстве и её взаимодействие с другими функциями безопасности Android.

Высокоуровневый дизайн

Подписание на устройстве состоит из двух основных компонентов:

  • odrefresh входит в состав модуля ART Mainline. Он отвечает за генерацию артефактов времени выполнения. Он проверяет существующие артефакты на соответствие установленной версии модуля ART, jar-файлам bootclasspath и jar-файлам системного сервера, чтобы определить, актуальны ли они или требуют повторной генерации. Если требуется повторная генерация, odrefresh генерирует их и сохраняет.

  • odsign — это двоичный файл, входящий в состав платформы Android. Он запускается во время ранней загрузки, сразу после монтирования раздела /data . Его основная задача — вызвать odrefresh для проверки необходимости создания или обновления каких-либо артефактов. Для любых новых или обновлённых артефактов, создаваемых odrefresh , odsign вычисляет хеш-функцию. Результат такого вычисления хеш-функции называется дайджестом файла . Для всех уже существующих артефактов odsign проверяет соответствие дайджестов существующих артефактов дайджестам, которые odsign вычислил ранее. Это гарантирует, что артефакты не были подменены.

В случае возникновения ошибок, например, если дайджест файла не совпадает, odrefresh и odsign удаляют все существующие артефакты в каталоге /data и пытаются сгенерировать их заново. Если это не удаётся, система возвращается в режим JIT.

odrefresh и odsign защищены dm-verity и являются частью проверенной цепочки загрузки Android.

Вычисление дайджестов файлов с помощью fs-verity

fs-verity — это функция ядра Linux, которая выполняет проверку данных файла на основе дерева Меркла. Включение fs-verity для файла заставляет файловую систему построить дерево Меркла на основе данных файла с использованием хешей SHA-256, сохранить его в скрытом месте рядом с файлом и пометить файл как доступный только для чтения. fs-verity автоматически проверяет данные файла по дереву Меркла по запросу при чтении. fs-verity делает корневой хеш дерева Меркла доступным в виде значения, называемого дайджестом файла fs-verity , и fs-verity гарантирует соответствие любых данных, считанных из файла, этому дайджесту.

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

На устройствах, ядро ​​которых не поддерживает fs-verity, odsign возвращается к вычислению дайджестов файлов в пространстве пользователя. odsign использует тот же алгоритм хеширования на основе дерева Меркла, что и fs-verity, поэтому дайджесты в обоих случаях одинаковы. fs-verity требуется на всех устройствах, работающих под управлением Android 11 и выше.

Хранение дайджестов файлов

odsign хранит дайджесты артефактов в отдельном файле odsign.info . Чтобы гарантировать сохранность odsign.info , odsign.info подписан ключом подписи, обладающим важными свойствами безопасности. В частности, этот ключ можно сгенерировать и использовать только во время ранней загрузки, когда выполняется только доверенный код; подробности см. в разделе «Доверенные ключи подписи» .

Проверка дайджестов файлов

При каждой загрузке, если odrefresh определяет, что существующие артефакты актуальны, odsign проверяет, не были ли файлы изменены с момента их создания. odsign делает это, проверяя дайджесты файлов. Сначала он проверяет подпись odsign.info . Если подпись действительна, odsign проверяет, соответствует ли дайджест каждого файла соответствующему дайджесту в odsign.info .

Доверенные ключи подписи

В Android 12 представлена ​​новая функция хранилища ключей, называемая ключами этапа загрузки, которая решает следующие проблемы безопасности:

  • Что мешает злоумышленнику использовать наш ключ подписи для подписи своей собственной версии odsign.info ?
  • Что мешает злоумышленнику сгенерировать собственный ключ подписи и использовать его для подписи своей версии odsign.info ?

Ключи этапа загрузки разделяют цикл загрузки Android на уровни и криптографически привязывают создание и использование ключа к определенному уровню. odsign создает свой ключ подписи на раннем уровне, когда работает только доверенный код, защищенный с помощью dm-verity .

Уровни загрузки нумеруются от 0 до магического числа 1000000000. Во время загрузки Android можно повысить уровень загрузки, установив системное свойство в init.rc Например, следующий код устанавливает уровень загрузки 10:

setprop keystore.boot_level 10

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

odsign использует уровень загрузки 30, и создаваемый им ключ подписи привязан к этому уровню загрузки. Прежде чем использовать ключ для подписи артефактов, odsign проверяет, привязан ли он к уровню загрузки 30.

Это предотвращает две атаки, описанные ранее в этом разделе:

  • Злоумышленники не смогут воспользоваться сгенерированным ключом, поскольку к тому времени, когда у злоумышленника появится возможность запустить вредоносный код, уровень загрузки превысит 30, и Keystore отклоняет операции, использующие этот ключ.
  • Злоумышленники не могут создать новый ключ, поскольку к тому времени, как злоумышленник успевает запустить вредоносный код, уровень загрузки уже превышает 30, и хранилище ключей отказывается создавать новый ключ с этим уровнем загрузки. Если злоумышленник создаёт новый ключ, не привязанный к уровню загрузки 30, odsign отклоняет его.

Keystore обеспечивает правильное применение уровня загрузки. В следующих разделах более подробно описывается, как это реализовано в различных версиях KeyMint (ранее Keymaster).

Реализация Keymaster 4.0

Разные версии Keymaster по-разному реализуют ключи загрузки. На устройствах с Keymaster 4.0 TEE/StrongBox Keymaster реализует это следующим образом:

  1. При первой загрузке Keystore создаёт симметричный ключ K0 с тегом MAX_USES_PER_BOOT , равным 1 Это означает, что ключ можно использовать только один раз за загрузку.
  2. Во время загрузки, если уровень загрузки повышается, новый ключ для этого уровня загрузки может быть сгенерирован из K0 с помощью функции HKDF: Ki+i=HKDF(Ki, "some_fixed_string") . Например, при переходе с уровня загрузки 0 на уровень загрузки 10 функция HKDF вызывается 10 раз для получения K10 из K0.
  3. При изменении уровня загрузки ключ для предыдущего уровня загрузки стирается из памяти, а ключи, связанные с предыдущими уровнями загрузки, становятся недоступными.

    Ключ K0 — это ключ MAX_USES_PER_BOOT=1 . Это означает, что его невозможно использовать и позже во время загрузки, поскольку всегда происходит как минимум один переход на другой уровень загрузки (на последний).

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

Реализация Keymaster 4.1 и KeyMint 1.0

Реализации Keymaster 4.1 и KeyMint 1.0 во многом идентичны реализации Keymaster 4.0. Основное отличие заключается в том, что K0 — это не ключ MAX_USES_PER_BOOT , а ключ EARLY_BOOT_ONLY , представленный в Keymaster 4.1. Ключ EARLY_BOOT_ONLY можно использовать только на ранних этапах загрузки, когда не выполняется недоверенный код. Это обеспечивает дополнительный уровень защиты: в реализации Keymaster 4.0 злоумышленник, скомпрометировавший файловую систему и SELinux, может изменить базу данных Keystore, создав собственный ключ MAX_USES_PER_BOOT=1 для подписи артефактов. Такая атака невозможна в реализациях Keymaster 4.1 и KeyMint 1.0, поскольку ключи EARLY_BOOT_ONLY можно создать только на ранних этапах загрузки.

Открытый компонент доверенных ключей подписи

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

Чтобы предотвратить эту атаку, odsign создаёт дополнительный ключ HMAC с тем же уровнем загрузки, что и ключ подписи. Затем, при создании ключа подписи, odsign использует этот ключ HMAC для создания подписи открытого ключа и сохраняет её на диске. При последующих загрузках, при извлечении открытого ключа ключа подписи, odsign использует ключ HMAC для проверки соответствия подписи на диске и полученного открытого ключа. Если они совпадают, открытый ключ является надёжным, поскольку ключ HMAC может использоваться только на ранних уровнях загрузки и, следовательно, не мог быть создан злоумышленником.