31.10.2018
TrustZone: доверенная ОС и ее приложения
Интернет-портал Habr.com, октябрь, 2018 <br>
Статья с упоминанием компании "Аладдин Р.Д."
Статья с упоминанием компании "Аладдин Р.Д."
В прошлых статьях мы рассматривали аппаратное устройство TrustZone и работу механизма Secure Monitor. Сегодня речь пойдет о TEE и ее приложениях. И если прошлый раз были довольно низкоуровневые вещи, сейчас все будет на вполне высоком уровне — на уровне операционной системы.
Пример урезанного TEE можно наблюдать сейчас в проекте ARM Trusted Firmware-M для нового поколения микроконтроллеров Cortex-M на платформе ARMv8-M. Это урезанная TEE, сейчас там есть поддержка микроконтроллеров на ядрах Cortex-M23 и Cortex-M33. Это flash-based микроконтроллеры, примерно эквивалентные Cortex-M0 и Cortex-M3, но с поддержкой TrustZone. У них мало ОЗУ, программа выполняется преимущественно из Flash, и поэтому в TEE нет динамической загрузки программ. На данный момент TF-M еще и однопоточная.
Здесь трастлет используется для электронной подписи документов. Программа из Linux вызывает трастлет, для чего последовательно создается контекст TEE, сеанс с трастлетом, передаются данные для подписи, и возвращается электронная подпись.
Что такое TEE
Что же такое TEE? Это доверенная среда исполнения (Trusted Execution Environment), в первую очередь — это среда исполнения программ. Опишем ее в терминах функции и свойств, но не в смысле программирования, а в философском смысле. Например, у поезда дальнего следования, электрички и такси одна самая главная функция – перевозить людей. А вот по свойствам они отличаются, например: поезд возит между городами, электричка — за город, а такси — преимущественно по городу. Поезд и электричка по билетам, такси — нет. И так далее. Функция TEE — доверенно хранить для нас некоторые данные и запускать для нас приложения. Мы хотим передавать TEE команды: запустить такое-то приложение, взять такие-то данные и сделать с ними то и это. При этом код приложения видеть не можем, равно как и данные. Мы будем только получать результат. Взаимодействие с TEE очень похоже на RPC. Эта функция идеально подходит для разной криптографии, например, для электронной подписи: ключи хранятся в TEE, и мы просим TEE подписать переданные данные хранимым в TEE ключом. Мы получаем результат, но доступа к ключу не имеем. У TEE есть ряд свойств, но основные таковы: a) мы доверяем ее реализации, и б) она надежно отделена от основной ОС устройства, защищена, ее сложно нарушить или сломать. Есть и другие свойства, но мы называем ее доверенной ОС именно за это. Свойство б) самое главное — TEE отделена, и ее сложно нарушить, то есть она защищена. Если смотреть на TEE через призму функций и свойств, то становится ясно, что TEE — это даже не совсем про TrustZone. TrustZone – это один из способов отделения TEE от основной (гостевой) ОС.Варианты реализации TEE
Если главные свойства TEE — что она отделена и ее сложно нарушить, то мы можем придумать разные варианты реализации TEE.- Использовать TrustZone — мы получаем разделение TEE и основной ОС в рамках одного ядра процессора.
- Запустить TEE на отдельном ядре в рамках системы на кристалле и общаться с ней через аппаратный интерфейс. В некоторых специализированных процессорах есть отдельные доверенные ядра для выполнения TEE, но в магазине их не купить, увы. Но можно и взять двухъядерный кристалл, например, Cortex-A+Cortex-M0/M4 и запустить на Cortex-M TEE.
- Запустить TEE в отдельном чипе и устанавливать с ним защищенное соединение через внешний интерфейс, например, SPI или SMbus. Для защиты коммуникации использовать криптографические методы. Этот метод используется, когда вы устанавливаете соединение с смарт-картой, например, чипованной пластиковой платежной картой. В каком-то смысле в чипе исполняется TEE, ведь по нашей просьбе она очень доверенно делает финансовые транзакции, хранит данные и т. п. Этот же метод используется в TPM (Trusted Platform Module) современной архитектуры PC.
TEE как ОС
В прошлых статьях мы все время называли TEE доверенной ОС и говорили, что она во многом похожа на настоящие операционные системы. Не претендуя на общность, скажем, что в основной массе TEE имеют:- приложения и процессы: TEE может загружать приложения и выполнять их;
- разделение памяти процессов и ядра: используется MMU для защиты пространства памяти процессов и для защиты памяти ядра TEE;
- потоки, взаимодействие процессов;
- хранение данных.
Программный интерфейс TEE
Для взаимодействия с другими программными компонентами у TEE есть API:- TEE предоставляет API для программ через системные вызовы (Supervisor Call, команда SVC);
- TEE дает API для Normal World через вызовы Secure Monitor (команда SMC).
Отличия TEE от обычной ОС
Два главных отличия TEE от Linuх и других знакомых нам ОС общего применения: 1. TEE выполняет действия не по команде пользователя, а по команде из Normal World; 2. TEE в TrustZone не имеет собственного планировщика. В обычной ОС пользователь генерирует некоторый ввод — вводит команды, щелкает мышкой по иконкам, и ОС обрабатывает этот ввод, передает его программам, а программы его обрабатывают. В серверном варианте ввод идет не от пользователя, а от неких клиентов, скорее всего, по сети. Но ОС, тем не менее, действует исходя из внешних входных данных. TEE же не обрабатывает внешние данные и не передает их приложениям. Вместо этого она обрабатывает команды и данные, переданные из Normal World через TEE Client API, и на этом почти все. Получается, что TEE выступает для ОС как некоторая библиотека с интерфейсом RPC, функции которой вызываются. После обработки функций TEE может ничего не делать. Второе отличие вытекает из первого. TEE в TrustZone делит процессорное время с Normal World и вызывается как библиотека. TEE не выделяет под себя процессорное время постоянно, она тратит столько времени, сколько ей нужно для выполнения запроса и потом передает управление в Normal World. А раз так, то она и не должна иметь своего планировщика — ей достаточно планировщика гостевой ОС. Планировщик основной ОС передает управление в TEE косвенно:- планировщик ставит на выполнение задачу;
- задача вызывает системный вызов ядра;
- системный вызов вызывает TEE, если это нужно;
- TEE работает столько, сколько необходимо для выполнения запроса и возвращает управление в Normal World.
Приложения TEE
Приложения, работающие в TEE, называются трастлетами — по аналогии с апплетами, которые работают в смарт-картах. Цитата из Википедии:Applet (англ. applet от application — приложение и -let — уменьшительный суффикс) — это несамостоятельный компонент программного обеспечения, работающий в контексте другого, полновесного приложения, предназначенный для одной узкой задачи и не имеющий ценности в отрыве от базового приложения.Trustlet — это Trusted Applet. Это программа для TEE, как мы уже выяснили, общается она с TEE через системные вызовы, у нее есть жизненный цикл и т. п. Но все равно название указывает, что это несамостоятельный компонент. Здесь несамостоятельность выражается в том, что трастлет будет выполнять вызовы из Normal World, а потом отключаться вместе с TEE. Если он закрутится в бесконечном цикле, ядро процессора перестанет выполнять функции ОС, и все в конечном счете повиснет. А вот программа для обычной ОС может крутиться в бесконечном цикле и майнить считать какие-то задачи, это совершенно нормально для программы. В этом плане она самостоятельнее трастлета. Трастлет должен иметь какой-то идентификатор, чтобы Normal World мог его называть. Принято давать трастлетам в качестве имени UUID — уникальные идентификаторы.
Жизненный цикл трастлета
Рассмотрим, как происходит запуск трастлета и выполнение команд. Логично было бы загрузить трастлет в память и начать работать, но в GlobalPlatform TEE Client API для запуска трастлета нужно создать контекст и установить сеанса работы с трастлетом. Создание контекста (context) — это установление соединения между программой Normal World и TEE. При этом спецификация GlobalPlatform предполагает, что в устройстве может быть несколько TEE, и на момент создания контекста можно выбрать, к какой TEE обратиться. В GlobalPlatform TEE Client API для этого предусмотрена функция: $$display$$TEEC_Result TEEC_InitializeContext(const char* name, TEEC_Context* context)$$display$$ Эта функция вызывается из приложения Normal World. Здесь name указывает на выбираемую TEE. Eсли мы хотим TEE по умолчанию или уверены, что у нас только одна TEE — подставляем NULL. В context сохраняется созданный контекст. После создания контекста нужно установить сеанс работы с трастлетом. Тут нам пригодится UUID трастлета. Для этого вызывается функция: $$display$$TEEC_Result TEEC_OpenSession( TEEC_Context* context,TEEC_Session* session, const TEEC_UUID* destination, uint32_t connectionMethod, const void* connectionData, TEEC_Operation* operation, uint32_t* returnOrigin) $$display$$ Сеанс эквивалентен работе с экземпляром программы в обычной ОС: в ОС может быть много экземпляров одной программ, и они будут работать независимо. А в TEE есть много сеансов, и по сути это подключения к уникальным экземплярам трастлета в памяти. При этом область кода будет, скорее всего, одна и та же, отображенная через MMU в память разных процессов. А вот область данных будет у каждого процесса своя, позволяя экземплярам работать независимо. Прямо как в Linux. При вызове TEEC_OpenSession контекст «context» и UUID трастлета «destination» передаются как входные данные. Установленный сеанс будет сохранен в «session». Некоторые параметры здесь и далее мы не будем рассматривать, они не так важны для понимания. В момент создания сеанса трастлет может быть загружен в память. Это то же, что происходит с приложениями в операционной системе. В большой TEE за это отвечает линковщик, он загружает бинарный образ трастлета, это такой подписанный ELF-файл. Если это маленькая TEE, трастлет должен быть уже загружен в память — он может быть статически слинкован или, для flash-микроконтроллеров, записан в flash-память по заданному адресу. Давайте предположим, что у нас большая TEE, и нужно загрузить трастлет в память. Откуда он берется? В принципе TEE на момент загрузки нужен объект с неким UUID, и механизм получения этого объекта может быть любой:- объект может быть уже в памяти;
- объект может быть размещен статически в flash-памяти (для flash-микроконтроллеров);
- объект может быть статически слинкован с TEE – для системных трастлетов;
- наконец, можно загрузить файл в ОЗУ с файловой системы, или даже по сети.
Supplicant
Выше мы задались вопросом: как TEE загружает данные с файловой системы или по сети? Если задуматься, TEE сама не имеет доступа к файловой системе ОС. То есть, TEE реализованная в TrustZone, могла бы иметь такой доступ, но тогда ей нужно было бы делить его с Normal World, а это не так-то просто. Например, Linux постоянно работает с файловой системой, и актуальное ее состояние есть только в памяти ядра Linux, а не на диске. Если TEE захочет вмешиваться и работать с файловой системой параллельно, это будет очень непросто. С сетевым обменом то же самое. Кроме того, TEE — довольно маленькая ОС, и реализовывать в ней драйверы низкого уровня для работы с носителями, с сетевым контроллером, поддерживать сетевой стек или драйвер ФС было бы накладно. Кроме того, это многократно увеличивает attack surface — был бы шанс взломать TEE, подсунув необычный inode на ext2 или что-то такое. Мы так не хотим. Поэтому при запуске ОС загружается так называемый Supplicant — программа-помощник. Она все время находится в соединении с TEE, и TEE использует ее для обращения к ресурсам Normal World. Поэтому, если TEE хочет загрузить образ трастлета с файловой системы, она обращается к Supplicant: $$display$$TEE: А подайте-с объект с UUID таким-то? Supplicant: (Загружает объект с файловой системы) Извольте-с! $$display$$ Конечно, такие обращения должны быть проверены на безопасность. В данном случае мы проверяем подпись в трастлете и почти ничем не рискуем — либо подпись верна и трастлет пойдет в работу, либо подпись неверна. То есть рискуем — трастлета может не оказаться, Supplicant может быть не запущен, но это уже другая часть модели угроз.Библиотека userspace
Программный интерфейс (вызовы TEEC_OpenSession и т. д.) реализуется с помощью библиотеки, которая транслирует вызов с уровня приложения в TEE. При реализации TEE в TrustZone для этого библиотека должна сначала передать вызов на уровень ядра ОС, так как только ядро ОС может вызывать SMC (Secure Monitor Call). В связке Linux + OP-TEE библиотекой userspace является libteec. Она транслирует вызовы GlobalPlatform TEE Client API в драйвер ядра через операции ioctl над файлом устройства: при запуске ОС загружается модуль ядра (драйвер), драйвер создает файл устройства. Открывая файл устройства с помощью libteec, пользовательская программа может работать с TEE Client API. То есть, работает такая конструкция: Приложение > libteec > файл устройства > драйвер ядра > SMC > TEE > трастлет.Пример работы трастлета
Вот как это работает в реальном применении:Здесь трастлет используется для электронной подписи документов. Программа из Linux вызывает трастлет, для чего последовательно создается контекст TEE, сеанс с трастлетом, передаются данные для подписи, и возвращается электронная подпись.