如何在日期时间格式的 OP-TEE 可信应用程序 (TA) 中获取 UTC 时间戳?

How to get a UTC timestamp in OP-TEE Trusted Application (TA) in datetime format?

免责声明:我花了整整 4-5 个小时寻找答案,在弄清楚答案后,我决定 post 在这里为同一个地方的人提供答案。

OP-TEE 是一个非常好的开发 TA 和 CA 的环境,但是,没有直接的方法来获取正确格式化的日期时间。也没有struct tm。因此,这让我想知道如何在 OP-TEE TA 中获取日期时间格式?

我花了很长时间尝试的是利用已经支持的 mbedTLS 库,对于新手来说,它们似乎确实支持获取日期时间格式。毕竟,他们确实有 gmtime 应该 return 这个值。

然而,不幸的是,gmtime 和相关函数在 ARMv8 平台上没有实现 OP-TEE。真倒霉。

那么如何在 OP-TEE TA 中获取 UTC 时间?

ARMv8 的所有 OP-TEE 开发都是使用 C 语言完成的。但是,它缺少主要的 libc 支持。实际上,它有非常少的库(例如 string.h),这些库是从原始 libc 对应库中略取的版本。

因此,OP-TEE 中提供的 <time.h> 仅包含 time_ttypedef,仅此而已。

问题可以分为两部分:

  1. 你如何获得自 1970 年 1 月 1 日以来的纪元 00:00:00?

这是一个有趣的问题,虽然直接的解决方案是简单地这样做:

TEE_Time tt;
TEE_GetREETime(&tt); 

对于许多不想依赖 REE(富执行环境,又名易受攻击的环境)计数的人来说,这个解决方案可能并不令人满意。这对于安全敏感的操作可能会有问题,在这些操作中,您需要时间才能合法,并且 REE 没有空间对其进行修改以执行某种攻击。

在上述情况下,您必须从硬件时钟获取纪元,这取决于您开发 TA 的硬件板。您甚至可以从位置设备中检索它,这些设备在 NMEA 语句中也是 return UTC 时间。虽然它可能不是 100% 精确到秒,但它可能就足够了。如果您需要非常高的精度,您将需要从硬件设备安全地获取它。

无论哪种方式,您都必须自己弄清楚如何获得纪元。本回答侧重于第二部分:获取日期时间。

  1. 从纪元中获取日期时间。解决步骤 1 后,您需要将其处理到日期时间。为此,您需要 OP-TEE 中不存在的 gmtime。它没有实现。你需要一个极简主义的实现来保持简单。

幸运的是,我找到了 this answer。其中链接到 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 文件夹中,并确保在 sub.mk 的源列表中添加 gmtime.c。最后,在 TA 本身你可以使用它:

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 结构并将 1900 添加到tm_year1tm_mon

与此同时,我希望这能找到有需要的人。