Android 4.4 и более поздних версий поддерживает проверенную загрузку с помощью дополнительной функции ядра device-mapper-verity (dm-verity), которая обеспечивает прозрачную проверку целостности блочных устройств. dm-verity помогает предотвратить появление постоянных руткитов, которые могут завладеть root-правами и поставить под угрозу устройства. Эта функция помогает пользователям Android быть уверенными, что при загрузке устройства оно находится в том же состоянии, в котором оно использовалось в последний раз.
Потенциально вредоносные приложения (PHA) с правами root могут скрываться от программ обнаружения или иным образом маскировать себя. Программное обеспечение для рутирования может сделать это, поскольку оно часто имеет более высокие привилегии, чем детекторы, что позволяет программному обеспечению «лгать» программам обнаружения.
Функция dm-verity позволяет вам просмотреть блочное устройство, базовый уровень хранения файловой системы, и определить, соответствует ли оно ожидаемой конфигурации. Это делается с помощью криптографического хеш-дерева. Для каждого блока (обычно 4 КБ) существует хэш SHA256.
Поскольку хеш-значения хранятся в дереве страниц, для проверки остальной части дерева необходимо доверять только «корневому» хешу верхнего уровня. Возможность изменить любой из блоков будет эквивалентна взлому криптографического хеша. См. следующую диаграмму для изображения этой структуры.
Открытый ключ включен в загрузочный раздел, который должен быть проверен производителем устройства снаружи. Этот ключ используется для проверки подписи этого хеша и подтверждения того, что системный раздел устройства защищен и не изменился.
Операция
Защита dm-verity находится в ядре. Таким образом, если рутирующееся программное обеспечение скомпрометирует систему до появления ядра, оно сохранит этот доступ. Чтобы снизить этот риск, большинство производителей проверяют ядро с помощью ключа, встроенного в устройство. Этот ключ невозможно изменить после того, как устройство покинет завод.
Производители используют этот ключ для проверки подписи загрузчика первого уровня, который, в свою очередь, проверяет подпись на последующих уровнях, загрузчике приложения и, в конечном итоге, в ядре. Каждый производитель, желающий воспользоваться преимуществами проверенной загрузки, должен иметь метод проверки целостности ядра. Предполагая, что ядро проверено, оно может просмотреть блочное устройство и проверить его при монтировании.
Один из способов проверки блочного устройства — напрямую хешировать его содержимое и сравнивать его с сохраненным значением. Однако попытка проверки всего блочного устройства может занять продолжительное время и потреблять большую часть энергии устройства. Устройствам потребуется много времени для загрузки, а затем они будут значительно разряжены перед использованием.
Вместо этого dm-verity проверяет блоки индивидуально и только при доступе к каждому из них. При чтении в память блок параллельно хешируется. Затем хэш проверяется вверх по дереву. А поскольку чтение блока — такая дорогостоящая операция, задержка, возникающая при этой проверке на уровне блока, сравнительно номинальна.
Если проверка не удалась, устройство генерирует ошибку ввода-вывода, указывающую, что блок не может быть прочитан. Похоже, что файловая система повреждена, как и ожидалось.
Приложения могут продолжить работу без полученных данных, например, если эти результаты не требуются для основной функции приложения. Однако если приложение не может продолжить работу без данных, оно завершается ошибкой.
Прямое исправление ошибок
В Android 7.0 и более поздних версиях надежность dm-verity повышается за счет прямой коррекции ошибок (FEC). Реализация AOSP начинается с обычного кода исправления ошибок Рида-Соломона и применяет метод, называемый чередованием, для уменьшения накладных расходов на пространство и увеличения количества поврежденных блоков, которые можно восстановить. Дополнительные сведения о FEC см. в разделе Строго принудительная проверенная загрузка с коррекцией ошибок .Выполнение
Краткое содержание
- Создайте образ системы ext4.
- Создайте хэш-дерево для этого изображения.
- Создайте таблицу dm-verity для этого хэш-дерева.
- Подпишите эту таблицу dm-verity, чтобы создать подпись таблицы.
- Объедините подпись таблицы и таблицу dm-verity в метаданные истинности.
- Объедините образ системы, метаданные истинности и хеш-дерево.
Подробное описание хеш-дерева и таблицы dm-verity см. в разделе «Проекты Chromium — проверенная загрузка» .
Создать хэш-дерево
Как описано во введении, хеш-дерево является неотъемлемой частью dm-verity. Инструмент cryptsetup генерирует для вас хэш-дерево. Альтернативно, совместимый определяется здесь:
<your block device name> <your block device name> <block size> <block size> <image size in blocks> <image size in blocks + 8> <root hash> <salt>
Для формирования хеша образ системы разбивается на уровне 0 на блоки по 4 КБ, каждому из которых назначается хэш SHA256. Уровень 1 формируется путем объединения только хэшей SHA256 в блоки размером 4 КБ, в результате чего изображение становится намного меньше. Уровень 2 формируется идентично, используя хэши SHA256 уровня 1.
Это делается до тех пор, пока хэши SHA256 предыдущего слоя не смогут поместиться в один блок. Когда вы получите SHA256 этого блока, у вас будет корневой хэш дерева.
Размер хеш-дерева (и соответствующее использование дискового пространства) зависит от размера проверяемого раздела. На практике размер хеш-деревьев обычно невелик, часто менее 30 МБ.
Если у вас есть блок в слое, который не полностью заполнен естественным образом хэшами предыдущего слоя, вам следует дополнить его нулями, чтобы получить ожидаемые 4 КБ. Это позволяет вам знать, что хэш-дерево не было удалено, а вместо этого заполнено пустыми данными.
Чтобы сгенерировать хеш-дерево, объедините хеши уровня 2 с хешами слоя 1, хэши слоя 3 с хэшами слоя 2 и так далее. Запишите все это на диск. Обратите внимание, что это не относится к уровню 0 корневого хеша.
Напомним, что общий алгоритм построения хэш-дерева следующий:
- Выберите случайную соль (шестнадцатеричная кодировка).
- Распакуйте образ вашей системы на блоки по 4 КБ.
- Для каждого блока получите свой (соленый) хэш SHA256.
- Объедините эти хеши, чтобы сформировать уровень
- Дополните уровень нулями до границы блока 4 КБ.
- Объедините уровень с вашим хеш-деревом.
- Повторяйте шаги 2–6, используя предыдущий уровень в качестве источника для следующего, пока у вас не останется только один хеш.
Результатом этого является один хеш, который является вашим корневым хешем. Это и ваша соль используются при построении таблицы сопоставления dm-verity.
Создайте таблицу сопоставления dm-verity.
Создайте таблицу сопоставления dm-verity, которая идентифицирует блочное устройство (или цель) для ядра и расположение хеш-дерева (которое имеет то же значение). Это сопоставление используется для создания и загрузки fstab
. В таблице также указаны размеры блоков и hash_start, начальное местоположение хеш-дерева (в частности, номер его блока от начала изображения).
См . cryptsetup для подробного описания полей таблицы сопоставления целей verity.
Подпишите таблицу dm-verity
Подпишите таблицу dm-verity, чтобы создать подпись таблицы. При проверке раздела сначала проверяется подпись таблицы. Это делается с помощью ключа вашего загрузочного образа в фиксированном месте. Ключи обычно включаются в системы сборки производителей для автоматического включения на устройствах в фиксированном месте.
Чтобы проверить раздел с помощью этой подписи и комбинации клавиш:
- Добавьте ключ RSA-2048 в формате, совместимом с libmincrypt, в раздел
/boot
по адресу/verity_key
. Определите местоположение ключа, используемого для проверки хеш-дерева. - В fstab соответствующей записи добавьте
verify
к флагамfs_mgr
.
Объединение подписи таблицы в метаданные
Объедините подпись таблицы и таблицу dm-verity в метаданные истинности. Весь блок метаданных имеет версии, поэтому его можно расширять, например, добавляя второй тип подписи или изменяя порядок.
В целях проверки работоспособности с каждым набором метаданных таблицы связывается магическое число, которое помогает идентифицировать таблицу. Поскольку длина включена в заголовок образа системы ext4, это дает возможность искать метаданные, не зная содержимого самих данных.
Это гарантирует, что вы не выбрали проверку непроверенного раздела. Если это так, отсутствие этого магического числа останавливает процесс проверки. Это число похоже на 0xb001b001
.
Значения байтов в шестнадцатеричном формате:
- первый байт = b0
- второй байт = 01
- третий байт = b0
- четвертый байт = 01
На следующей диаграмме показана разбивка метаданных истинности:
<magic number>|<version>|<signature>|<table length>|<table>|<padding> \-------------------------------------------------------------------/ \----------------------------------------------------------/ | | | | 32K block content
И эта таблица описывает эти поля метаданных.
Поле | Цель | Размер | Ценить |
---|---|---|---|
магическое число | используется fs_mgr для проверки работоспособности | 4 байта | 0xb001b001 |
версия | используется для версии блока метаданных | 4 байта | в настоящее время 0 |
подпись | подпись таблицы в дополненной форме PKCS1.5 | 256 байт | |
длина стола | длина таблицы dm-verity в байтах | 4 байта | |
стол | таблица dm-verity, описанная ранее | длина таблицы в байтах | |
прокладка | эта структура дополнена 0 и имеет длину 32 КБ. | 0 |
Оптимизация dm-verity
Чтобы получить максимальную производительность от dm-verity, вам необходимо:
- В ядре включите NEON SHA-2 для ARMv7 и расширения SHA-2 для ARMv8.
- Поэкспериментируйте с различными настройками упреждающего чтения и prefetch_cluster, чтобы найти лучшую конфигурацию для вашего устройства.