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

Начиная с Android 12, модуль Android Runtime (ART) является модулем Mainline . Обновление модуля может потребовать пересборки артефактов компиляции ahead-of-time (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, и Keystore отказывается создавать новый ключ с этим уровнем загрузки. Если злоумышленник создает новый ключ, который не привязан к уровню загрузки 30, odsign отклоняет его.

Keystore обеспечивает правильное применение уровня загрузки. В следующих разделах более подробно описывается, как это делается для разных версий 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 . Это означает, что этот ключ также невозможно использовать позже при загрузке, поскольку всегда происходит как минимум один переход на уровень загрузки (на конечный уровень загрузки).

Когда клиент Keystore, такой как odsign запрашивает создание ключа на уровне загрузки i , его blob шифруется ключом 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 извлекает компонент открытого ключа подписного ключа из Keystore. Однако Keystore не извлекает этот открытый ключ из TEE/SE, который содержит соответствующий закрытый ключ. Вместо этого он извлекает открытый ключ из своей собственной базы данных на диске. Это означает, что злоумышленник, который скомпрометировал файловую систему, может изменить базу данных Keystore, чтобы она содержала открытый ключ, который является частью пары открытого/закрытого ключей под его контролем.

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