Как получить метку времени UTC в доверенном приложении OP-TEE (TA) в формате даты и времени?

Отказ от ответственности: у меня ушло 4-5 часов на поиск ответа, и, выяснив его, я решил опубликовать его здесь для людей в том же месте.

OP-TEE - довольно хорошая среда для разработки ТА и ЦС, однако нет простого метода получения правильно отформатированной даты и времени. Здесь нет или. Поэтому это заставило меня задуматься, как мне получить формат даты и времени в OP-TEE TA?

Я долго пытался использовать уже поддерживаемые библиотеки, которые для новичка могут показаться поддерживающими получение формата datetime. В конце концов, у них есть то, что должно возвращать это значение.

Однако, к сожалению, а соответствующие функции не имеют реализации для платформы OP-TEE на ARMv8. Это довольно неприятная удача.

Так как же узнать время UTC в OP-TEE TA?

2 ответа

Вся разработка OP-TEE для ARMv8 выполняется с использованием C. Однако в нем отсутствует основная поддержка libc. На практике у него очень мало библиотек (например, string.h), которые представляют собой скимнутые версии из исходных соответствующих библиотек libc.

При этом предоставленные <time.h> в OP-TEE не содержится ничего, кроме typedef для time_t и это все.

Проблему можно разбить на два раздела:

  1. Как вы получаете эпохи с 1 января 1970 г., 00:00:00?

Это интересная проблема, и простое решение - просто сделать это:

      TEE_Time tt;
TEE_GetREETime(&tt); 

Это решение может быть неудовлетворительным для многих людей, которые не хотели бы полагаться на счет эпох REE (Rich Execution Environment, также известный как уязвимая среда). Это может быть проблематично для операций, чувствительных к безопасности, когда вам нужно время, чтобы быть законным, и у REE нет места, чтобы изменить его для выполнения определенной атаки.

В случае, описанном выше, вам нужно будет получить эпохи из аппаратных часов, которые будут зависеть от аппаратной платы, на которой вы разрабатываете TA. Вы даже можете получить его с устройств определения местоположения, которые также возвращают время UTC в заявлениях NMEA. Хотя это может быть не на 100% с точностью до секунды, этого может быть достаточно. Если вам нужна очень высокая точность, вам нужно будет безопасно получить ее с аппаратного устройства.

В любом случае, вы должны выяснить, как получить эпохи самостоятельно. Этот ответ сосредоточен на второй части: получении даты и времени.

  1. Получение даты и времени по эпохам. После того, как вы решили шаг 1, вам нужно обработать его до datetime. Для этого вам понадобится gmtimeкоторого нет в OP-TEE. Реализации нет. И вам нужна минималистичная реализация, чтобы все было просто.

К счастью, мне удалось найти ответ . Какие ссылки на newlibбиблиотеки, разработанные для Free BSD, которые идеально подходят для встраиваемых систем. Следовательно, почему это полезно здесь!

Мне удалось собрать его из их реализации, и вы можете использовать его здесь:

gmtime_r.h:

      #include <inttypes.h>

#define SECSPERMIN  60L
#define MINSPERHOUR 60L
#define HOURSPERDAY 24L
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY  (SECSPERHOUR * HOURSPERDAY)
#define DAYSPERWEEK 7
#define MONSPERYEAR 12

#define YEAR_BASE   1900
#define EPOCH_YEAR      1970
#define EPOCH_WDAY      4
#define EPOCH_YEARS_SINCE_LEAP 2
#define EPOCH_YEARS_SINCE_CENTURY 70
#define EPOCH_YEARS_SINCE_LEAP_CENTURY 370

#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)


typedef int64_t time_t;

struct tm
{
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
};

struct tm* gmtime_r (time_t tim_p, struct tm* res);

gmtime_r.c:

      #include "gmtime_r.h"

#define EPOCH_ADJUSTMENT_DAYS   719468L
/* year to which the adjustment was made */
#define ADJUSTED_EPOCH_YEAR 0
/* 1st March of year 0 is Wednesday */
#define ADJUSTED_EPOCH_WDAY 3
/* there are 97 leap years in 400-year periods. ((400 - 97) * 365 + 97 * 366) */
#define DAYS_PER_ERA        146097L
/* there are 24 leap years in 100-year periods. ((100 - 24) * 365 + 24 * 366) */
#define DAYS_PER_CENTURY    36524L
/* there is one leap year every 4 years */
#define DAYS_PER_4_YEARS    (3 * 365 + 366)
/* number of days in a non-leap year */
#define DAYS_PER_YEAR       365
/* number of days in January */
#define DAYS_IN_JANUARY     31
/* number of days in non-leap February */
#define DAYS_IN_FEBRUARY    28
/* number of years per era */
#define YEARS_PER_ERA       400

struct tm* gmtime_r (time_t tim_p, struct tm* res)
{
    time_t days, rem;
    const time_t lcltime = tim_p;
    int era, weekday, year;
    unsigned erayear, yearday, month, day;
    unsigned long eraday;

    days = lcltime / SECSPERDAY + EPOCH_ADJUSTMENT_DAYS;
    rem = lcltime % SECSPERDAY;
    if (rem < 0)
        {
        rem += SECSPERDAY;
        --days;
        }

    /* compute hour, min, and sec */
    res->tm_hour = (int) (rem / SECSPERHOUR);
    rem %= SECSPERHOUR;
    res->tm_min = (int) (rem / SECSPERMIN);
    res->tm_sec = (int) (rem % SECSPERMIN);

    /* compute day of week */
    if ((weekday = ((ADJUSTED_EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
        weekday += DAYSPERWEEK;
    res->tm_wday = weekday;

    /* compute year, month, day & day of year */
    /* for description of this algorithm see
    * http://howardhinnant.github.io/date_algorithms.html#civil_from_days */
    era = (days >= 0 ? days : days - (DAYS_PER_ERA - 1)) / DAYS_PER_ERA;
    eraday = days - era * DAYS_PER_ERA; /* [0, 146096] */
    erayear = (eraday - eraday / (DAYS_PER_4_YEARS - 1) + eraday / DAYS_PER_CENTURY -
        eraday / (DAYS_PER_ERA - 1)) / 365; /* [0, 399] */
    yearday = eraday - (DAYS_PER_YEAR * erayear + erayear / 4 - erayear / 100); /* [0, 365] */
    month = (5 * yearday + 2) / 153;    /* [0, 11] */
    day = yearday - (153 * month + 2) / 5 + 1;  /* [1, 31] */
    month += month < 10 ? 2 : -10;
    year = ADJUSTED_EPOCH_YEAR + erayear + era * YEARS_PER_ERA + (month <= 1);

    res->tm_yday = yearday >= DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY ?
        yearday - (DAYS_PER_YEAR - DAYS_IN_JANUARY - DAYS_IN_FEBRUARY) :
        yearday + DAYS_IN_JANUARY + DAYS_IN_FEBRUARY + isleap(erayear);
    res->tm_year = year - YEAR_BASE;
    res->tm_mon = month;
    res->tm_mday = day;

    res->tm_isdst = 0;

    return (res);
}

Вы можете поместить оба этих файла в свою папку TA и обязательно добавить gmtime.c в списке источников в sub.mk. И, наконец, в самом ТА вы можете его использовать:

      TEE_Time tt; 
TEE_GetREETime(&tt);
struct tm *lt, temp;
lt = gmtime((time_t)tt.seconds, &temp);
DMSG("%4d-%2d-%2d %2d:%2d:%2d", lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec); 

Это напечатает время и дату в правильном формате.

В настоящее время я еще не портировал реализацию strftime, но скоро я сделаю и это, что автоматически отформатирует tm struct и добавить 1900 к tm_year и 1 к tm_mon.

А пока я надеюсь, что это найдет кого-нибудь в нужде.

Таймер безопасности

Чтобы получить текущее время в миллисекундах с 1 января 1970 года, вызовите . Уровень безопасности зависит от того, какие безопасные часы доступны в конкретной системе. Абсолютная минимальная гарантия заключается в том, что это значение не может уменьшиться во время работы доверенного приложения, но REE может контролировать, с какой скоростью это происходит. Гарантии на перезагрузку нет.

OP-TEE поставляется с реализацией TEE_GetSystemTimeна основе CNTPCT. Его можно настроить так, чтобы он был эксклюзивным для безопасного мира TrustZone на любом чипе armv8, но я не знаю, действительно ли все производители чипов делают это.

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

Многие платформы не могут надежно хранить дату, потому что для этого требуются часы, которые всегда включены, а для этого требуется батарея, которая не разряжается. Часто ненадежный мир может договориться о том, чтобы установить время на произвольное значение после перезагрузки системы. Поэтому, если вам нужна текущая дата с достаточной точностью, чтобы убедиться, что срок действия сертификата не истек, этого недостаточно, и вам нужно сделать что-то вроде установки соединения с безопасным сервером времени. Это проблема начальной загрузки, поскольку вы не можете убедиться, что срок действия сертификата сервера времени не истек.

Разбивая время

OP-TEE предоставляет тонкую библиотеку C, не включающую ничего похожего на . Если вы хотите рассчитать дату и время по эпохе, вам необходимо предоставить собственную реализацию .

Mbed TLS является потребителем, а не поставщикомgmtime(точнееgmtime_r). Эта функция используется для проверки действительности сертификатов.

Другие вопросы по тегам