Немного о ARM Security Extensions (aka ARM TrustZone)
На Хабре уже несколько раз упоминали о SMM — режиме процессора x86/64 который имеет больше привилегий чем даже режим гипервизора. Нечто подобное есть и в процессорах архитектуры ARMv7 и ARMv8. Вычислительные ядра этих архитектур могут иметь опциональное расширение под названием ARM Security Extensions, которое позволит разделить исполняемый код, память и периферию на два домена — доверенный и недоверенный. Официальное маркетинговое название этой технологии — ARM TrustZone. Но технари чаще предпочитают говорить о security extensions.
Это будет обзорная статья, поэтому я не буду вдаваться в глухие технические дебри. Тем не менее технические детали будут присутствовать. Первая часть статьи будет посвящена вопросу зачем это всё вообще нужно, а вторая — как это работает в общих чертах. Если общество заинтересуется — следующая статья будет содержать больше технических деталей. Кому интересно — добро пожаловать под кат.
Зачем вообще нужны ARM Security Extensions
Хотя в названии и есть слово «security», эти расширения нужны не только реализации функций связанных с безопасностью в привычном понимании этого слова. Когда мы говорим «безопасность» мы обычно думаем о шифровании, ограничении доступа, TPM, безопасных хранилищах и тому подобных вещах. Security extensions позволяют реализовать эти все функции, но так же они используются для таких вещей, как например запуск и остановка процессорных ядер, перезагрузка системы и т.д.
Не совсем security функции
Все мы знаем что есть такая штука как BIOS (а теперь и EFI). Их задачей является первичная инициализация системы, загрузка OS, предоставление OS информации о системе, помощь OS в управлении компьютером.
Исторически сложилось так, что на ARM-based платформах нет (точнее не было) аналога BIOS. Обычно в ROM-коде присутствовал простенький загрузчик который находил и загружал полноценный загрузчик. Часто таким полноценным загрузчиком выступает u-boot. U-boot в свою очередь инициировал минимум периферии (например запускал контроллер DRAM), находил и запускал ядро linux. Ядро linux в свою очередь могло полностью вытереть из памяти u-boot, потому что он больше не нужен. И всё, после загрузки ядро было полностью предоставлено себе. Прямо в него (или в сбоку, в специальную структуру под названием device tree) была зашита вся информация о системе где оно работает. Все операции по управлению системой (например разгон процессора) ядру приходилось выполнять самому.
Это было неудобно по ряду разных причин. Например, производителям не хочется отдавать разработчикам ядра контроль над всякими «нежными» модулями типа кешей и шины. Или, например, процедура перезагрузки чипа требует каких-то хитрых манипуляций которые неудобно производить из кода ядра.
Короче, потребность в чем-то вроде BIOS существует. Только в ARM-системах он называет Secure Monitor и работает благодаря Security Extensions. По моему опыту чаще всего он используется для запуска и остановки дополнительных ядер в многопроцессорной системе, управления шиной, переброски выполняющегося кода между мощными и слабыми ядрами в ахритектуре big.LITTLE, активации режима гипервизора и тому подобных деликатных вещей.
Security функции
Security extensions позволяют запустить сбоку ещё одну OS со своим ядром и пользовательскими приложениями. Её обычно называют Trusted OS. У этой OS будет своя защищенная память и доступ к защищенной же периферии, например крипто-аккселераторам. Двумя примерами таких OS является гугловая Trusty и опенсурсная OP-TEE. Существует консорциум Global Platform который выработал общие требования к таким OS под названием Trusted Execution Environment.
Гугл, например, использует свою OS для безопасного хранения ключей (андроидный сервис keymaster). Motorola в своё время использовала проприетарную реализацию безопасной OS для DRM. Там была вообще фантастическая система: пользователь мог хранить и проигрывать DRM-видео, при этом даже андроидное ядро ни в какой момент не могло получить доступ к памяти где хранились разжатые (или хотя бы расшифрованные) видео-фреймы. Но при этом Андроид мог, например, рисовать элементы управления поверх такого видео.
В защищенную OS можно загружать пользовательские приложения (естественно подписанные). Это позволяет например гонять там банковское приложение для платежей через NFC или виртуальную SIM-карту. Правда, скажу честно, на практике я с таким пока не сталкивался.
Так же secure extensions позволяют организовать Secure Boot: корнем доверия является ROM-код, который проверяет подпись следующего загрузчика, загрузчик может проверить подпись ядра обратившись к Secure Monitor. Ядро в свою очередь тоже может проверять подписи исполняемого кода. Таким образом основные компоненты будут защищены от изменения.Технология спорная, но некоторые разработчики android-устройств её очень любят.
О том как это всё работает
Информация о технических деталях открыта. Можно скачать ARM Technical Reference Manual и прочитать всё самому. Кроме того в Сети хватает всяких красивых презентаций. Я не буду вдаваться в глубокие дебри, просто опишу основные идеи. Я буду использовать терминологию из ARMv8, потому что она более последовательна, в сравнении с терминами принятыми в ARMv7.
Режимы процессора
В этом разделе будет приведено немного технических деталей. Если системное программирование для вас абсолютно темная тема — этот раздел можно и пропустить.
Собственно, все наверное слышали про кольца защиты в x86. В ARM-ах есть точно такая же штука, только тут они называются Exception Levels (сокращенно EL). Их может быть от двух до четырех (или шести, смотря как считать). Это режимы работы процессора. Чем выше Exception Level, тем большими привилегиями обладает код исполняющийся в нем.
Все ядра ARMv7 или ARMv8 поддерживают минимум два из них: EL0 и EL1.
На EL0 работают пользовательские программы (например ваш браузер). У кода исполняющегося в EL0 нет никаких привилегий: он (обычно) не может работать с периферией, перенастраивать MMU, запрещать (и разрешать тоже) прерывания, обрабатывать исключительные ситуации. Но браузеру этого всего и не нужно. А если всё-таки нужно, то об этом надо попросить ядро OS.
На EL1 работают ядро и драйвера. У них, соответственно, есть возможность работать с периферией, программировать MMU и далее по списку. Еще лет 10 назад этого было бы вполне достаточно. Но технологии не стоят на месте.
Ещё два EL могут появится если процессорное ядро включает дополнительные расширения: virtualization extensions и security extensions. При чем оба эти расширения опциональны (хотя и присутствуют во всех современных чипах) и процессор может иметь или любое из них, либо оба сразу. Немалая часть ARM Technical Reference Manual посвящена взаимодействию этих расширений.
Так, если если в ядре есть virtualization extensions, то появляется EL2. На этом уровне работает гипервизор. Так же MMU становится двухстадийным и появляется виртуальный контроллер прерываний. Но это совсем другая история которую нужно рассказывать отдельно.
Если в процессоре присутствуют security extensions, то появляется режим EL3 и режимы S-EL0 и S-EL1. Кроме того появляется понятие secure mode (и соответственно non-secure mode) — это режимы процессора ортогональные exception levels. EL3 обладает теми же привилегиями что и EL2, плюс ещё одной особенностью. Только в режиме EL3 код может переключить процессор между secure и non-secure mode. В чем же между ними разница? А разница только в значении одного бита — NS.
Дело в том, что security extensions — это не только расширения для вычислительного ядра. Эти расширения так же затрагивают MMU, контроллер прерываний и контроллер шины. Например, контролер шины проверяет значение этого самого бита NS при доступе к памяти и периферии. Если участок памяти помечен как secure, то доступ к нему можно получить только из secure mode. Это значит, что ни ядро обычной OS, ни гипервизор не смогут прочитать/изменить «безопасную» память. То же самое и с периферией. Если приходит прерывание которое помечено как secure, то это прерывание будет обрабатывать не обычная OS, а trusted OS из режима secure mode.
Получается, будто в одном процессоре живут два мира: normal world и secure world (это термины из официальной документации, если что). При чем secure world может вмешиваться в дела normal world, но не наоборот. В режиме EL3 работает secure monitor, который служит эдаким Хароном — позволяет попасть из одного мира в другой.
Как вы уже наверное догадались, режимы S-EL0 и S-EL1 — это аналоги EL0 и EL1 из normal world. В S-EL1 бегает ядро «безопасной» OS, а в S-EL0 — приложения (например та же виртуальная SIM-карта). На картинке выше используется терминология из ARMv7, но понять что к чему довольно легко (я надеюсь).
Взаимодействие Normal World и Secure World
Процессор всегда запускается в secure-mode (потому что иначе он туда никак не попадет). Загружается и инициализируется Trusted OS, после чего процессор переходит в non-secure mode и загружает обычную OS.
Казалось бы при всех своих возможностях secure world должен занимать доминирующее положение в системе. Но на самом деле всё наоборот. Большую часть времени он неактивен. Только когда обычной OS нужны какие-то услуги, она обращается к secure monitor и управление переходит в secure world. Таким образом именно normal world решает когда будет работать secure world. Это сделано для того, что бы не мешать пользователю работать с устройством, смотреть видео и слушать музыку. Ведь если secure world заберет управление в неподходящий момент, то это может нарушить работу normal world.
На самом деле у secure world есть возможность получить управление в обход normal world, используя прерывания. Например можно завести таймер который будет периодически вызывать secure world. Но так обычно не делают по причинам обозначенным выше. User experience превыше всего.
Для ядра linux есть набор патчей который позволяет обычным приложениям взаимодействовать с приложениями работающими в Trusted OS, согласно спецификациям GlobalPlatform TEE, о которых я уже упоминал выше. Таким разработчики могут писать приложения которые (с незначительными изменениями) смогут работать на любой совместимой Trusted OS.
Уголок параноика
Может ли secure world следить за вами? Теоретически — да. На практике я имел доступ к проприетарным trusted OS, которые затем устанавливались на пользовательские устройства. В их коде я не видел таких функций. Что, конечно же, ни о чем не говорит.
Если на вашем устройстве активирован secure boot, то у вас проблемы. В том смысле, что вы ничего не сможете сделать с кодом бегающим в secure world. Правда, вы так же ничего не сможете сделать и с кодом бегающим в режиме ядра.
К счастью, SoC’и с включенным secure boot довольно редки и их стараются не продавать всем подряд. Поэтому у вас скорее есть возможность заменить trusted OS на свою. Правда, ещё остается ROM-код с которым вы не сможете сделать почти ничего. И если secure monitor зашит в ROM-код, то опять же у вас проблемы. Но, есть SoC’и, на которых вы сможете устанавливать свой secure monitor. Например — Renesas RCAR H3. Так что ещё не всё потеряно.
Для желающих поэкспериментировать — есть возможность поднять Trusted OS на Raspberry PI. Как это сделать — написано в документации к OP-TEE.