硬件介绍
NuMaker-IoT-M487 开发板以 NuMicro M487 系列微控制器为主控核心,主频 192MHz,512KB FLASH,160KB RAM。具体内容就不再复制粘贴了,请直接查看NuMaker-IoT-M487 上手指南。
首次拿到开发板,它的大红配色让人眼前一亮。
本评测着重在 Crypto 模块,将展示各 API 的用法,遇到的小“坑”,以及 M487 的一个(很可能)硬件缺陷BSP 配置错误的发现过程。
使用说明
参照RT-Thread Studio 快速上手指南可以很快创建出一个闪灯程序:不需要写一行代码,Studio 生成的项目就是一个闪灯程序。
温馨提示
拨码开关太小,最好准一个小螺丝刀。
功能测试
根据 M487 本身的硬件能力,本次评测将涉及、对称加密、散列、循环冗余检验等方面。
熟悉环境、API
RT-Thread 采用组件式架构,Crypto 组件的接口定义在 rt-thread\components\drivers\hwcrypto 目录。M487 通过两层代码实现 hwcrypto 接口:一层是原本的硬件驱动,另一层是 RTT porting 代码。
各 API 都是同步调用,所以注意不要在 main 里直接用,而是在一个 thread 里调用。
使用 Crypto 组件时,需要先创建/获取 rt_hwcrypto_device 对象,然后针对要使用的功能创建对应的上下文,使用完成后再销毁上下文。
准备工作
#define print rt_kprintf
// 辅助函数
void print_hex(const uint8_t *buffer, int len)
{
int i;
for (i = 0; i < len; i++)
print("%02X", buffer[i]);
print("\n");
}
void main_test(void *dummy)
{
struct rt_hwcrypto_device * dev = rt_hwcrypto_dev_default();
}
int main(int argc, char **argv)
{
rt_thread_t tid = rt_thread_create("t", main_test, NULL,
1024,
5,
50);
rt_thread_startup(tid);
return 0;
}
RNG
M487 内置硬件随机数发生器,这里我们生成一批数据,然后对数据的随机性做一个最最简单的检查:各字节是否在 [0..255] 之间均匀分布。
void test_rng(struct rt_hwcrypto_device *device)
{
struct rt_hwcrypto_ctx *ctx = rt_hwcrypto_rng_create(device);
int i;
static uint32_t counts[256] = {0};
#define CNT_THRES 700
print("generating 256 * 1000 random bytes:\n")
for (i = 0
{
uint32_t v = rt_hwcrypto_rng_update_ctx(ctx)
counts[v & 0xff]++
counts[v & 0xff]++
counts[v & 0xff]++
counts[v & 0xff]++
}
rt_hwcrypto_rng_destroy(ctx)
print("a simple check: if random bytes follow an uniform distribution...")
int flag = 0
for (i = 0
{
if (counts[i] < CNT_THRES)
flag = 1
}
print("%s\n", flag == 0 ? "PASS" : "FAIL")
}
这个函数的运行结果是令人吃惊的:测试失败!
简单检查 RTT Porting 代码可确认问题不在这一层。但是这个地方需要进一步讨论:TRNG 失败后,回退到 rand() 是否合适?
从现象看,或许是漏配了 TRNG 的 0/1 均匀分布开关,再查 M487 TRNG 寄存器定义,无此开关。这个问题只能由新唐解决了,拭目以待。
更新:M487 实际上无 TRNG。
温馨提示
目前不要使用 M487 生成真随机数,不可靠。
CRC
CRC 功能测试如下:
温馨提示
内置的几种 hwcrypto_crc_mode 不好用(不常见),建议直接构造配置,并使用 rt_hwcrypto_crc_cfg。
Hash
试试 SHA256:
void test_hash(struct rt_hwcrypto_device *device)
{
const static uint8_t result[] = {0x9f, 0x64, 0xa7, 0x47, 0xe1, 0xb9, 0x7f, 0x13,
0x1f, 0xab, 0xb6, 0xb4, 0x47, 0x29, 0x6c, 0x9b, 0x6f, 0x02, 0x01, 0xe7,
0x9f, 0xb3, 0xc5, 0x35, 0x6e, 0x6c, 0x77, 0xe8, 0x9b, 0x6a, 0x80, 0x6a};
int i;
struct rt_hwcrypto_ctx *ctx = rt_hwcrypto_hash_create(device, HWCRYPTO_TYPE_SHA256);
if (ctx == 0)
{
print("HWCRYPTO_TYPE_SHA256 not available!\n");
return;
}
const static uint8_t value[] = {1,2,3,4};
static uint8_t hash[256 / 8];
rt_err_t err = rt_hwcrypto_hash_update(ctx, value, sizeof(value));
if (err)
{
print("rt_hwcrypto_hash_update: %d\n", err);
}
rt_hwcrypto_hash_finish(ctx, hash, 32);
rt_hwcrypto_hash_destroy(ctx);
print("%s\n", memcmp(hash, result, sizeof(hash)) == 0 ? "PASS" : "FAIL");
}
测试结果令人“遗憾”:程序卡在 rt_hwcrypto_hash_update 里的。
温馨提示
NuMaker-IoT-M487 SDK v1.0.0 有问题,临时解决方案:将 BSP 代码到 main 分支的最新版本。升级时,务必注意只升级 Crypto 相关的二层代码即可,以免其它编译问题。
升级后,测试通过。
AES-128
用 AES-128 算法先加密再解密,看看结果是否一致:
void test_aes_128(struct rt_hwcrypto_device *device)
{
struct rt_hwcrypto_ctx *ctx = rt_hwcrypto_symmetric_create(device, HWCRYPTO_TYPE_AES_ECB);
if (ctx == 0)
{
print("HWCRYPTO_TYPE_AES_ECB not available!\n");
return;
}
const static uint8_t key[128 / 8] = {1,2,3,4};
const static uint8_t iv[16] = {4};
static uint8_t msg[16] = {5,6,7,8};
static uint8_t enc[16] = {};
static uint8_t dec[16] = {};
rt_hwcrypto_symmetric_setkey(ctx, key, sizeof(key) * 8);
rt_hwcrypto_symmetric_setiv(ctx, key, sizeof(iv));
rt_err_t err = rt_hwcrypto_symmetric_crypt(ctx, HWCRYPTO_MODE_ENCRYPT, sizeof(msg), msg, enc);
if (err)
{
print("ENCRYPT err: %d\n", err);
}
err = rt_hwcrypto_symmetric_crypt(ctx, HWCRYPTO_MODE_DECRYPT, sizeof(msg), enc, dec);
if (err)
{
print("DECRYPT err: %d\n", err);
}
rt_hwcrypto_symmetric_destroy(ctx);
print("%s\n", memcmp(dec, msg, sizeof(msg)) == 0 ? "PASS" : "FAIL");
}
心得与建议
Crypto 组件 API 设计延续 RT-Thread API 的简洁、明快,点赞;
建议 RT-Thread Studio 为新项目生成 .gitignore。
原作者:FLWX_7778