mbedtls 移植

mbedtls 移植

本文将详细描述如何将 mbedtls 移植到新的硬件平台,只描述移植思路,并不针对特定平台进行讲解。已在 STM32F7 硬件平台完成移植工作并使用 dtls (ECDHE-RSA / ECDHE-ECDSA) 示例代码对移植过程进行验证。

image

mbedtls 介绍

mbedtls 是一款开源软件加密库,使用 C 语言编写,采用模块化设计使得模块之间松散耦合,许可协议为 Apache 2.0。从功能上来看主要包括 加密库、X509 证书、SSL/TLS 协议三个组成部分,具体的特性介绍可以阅读 mbedtls core features

mbedtls 很适合应用于嵌入式系统中,可以作为 openssl替代者,相比 opensslmbedtls 的代码更加简洁,api 更加直观和易于理解。

mbedtls 可移植性分析

mbedtls 采用模块化设计,代码编写时使用宏定义的方式将平台依赖代码进行提取,用户将 mbedtls 移植到新的平台运行时只需要修改宏定义,添加平台依赖相关的代码即可,除了标准的 C 库外,以下模块可能需要根据需求进行修改:

  • 网络模块
  • 时间模块
  • 熵源模块
  • 硬件加速
  • 打印模块

如果不想对库文件进行修改可以通过添加宏定义的方式来添加用户的接口,下面会进行详细说明。

mbedtls 移植准备

下载 mbedtls ,将 library 目录下的源文件和 include 目录下的头文件拷贝到工作目录。移植示例中将使用 mbedtls-2.4 分支:

1
2
# 克隆 mbedtls-2.4 分支
git clone -b mbedtls-2.4 https://github.com/ARMmbed/mbedtls

mbedtls 移植过程

适配网络模块

网络模块 net_sockets.c 文件中对套接字接口进行了封装,用户可以将接口进行替换,替换后通过 mbedtls_ssl_set_bio() 接口来注册发送和接收接口,根据需要开启或关闭超时检测、阻塞/非阻塞功能。

适配时间模块

定时器回调

如果在系统中需要使用 DTLS 协议,则需要适配时间模块来提供以下接口:

1
2
int mbedtls_timing_get_delay( void *data );
void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms );

使用时需要通过mbedtls_ssl_set_timer_cb()接口进行设置,DTLS 协议为什么需要提供定时器回调接口可以参考 DTLS tutorial ,这里不做具体分析。如果不想改变 mbedtls 库源文件,可以通过注释掉 MBEDTLS_TIMING_C 定义的方式来添加自己的回调接口,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/** timing.c 替换开始 */
#include <**.h> // 硬件接口头文件
#include "mbedtls/timing.h"

unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
{
...... // 用户代码
}

/*
* Set delays to watch
*/
void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms )
{
...... // 用户代码
}

/*
* Get number of delays expired
*/
int mbedtls_timing_get_delay( void *data )
{
...... // 用户代码
}
/** timing.c 替换结束 */

另一种方法是开启 MBEDTLS_TIMING_C 定义,并添加 MBEDTLS_TIMING_ALT 定义,在 timing_alt.c / timing_alt.h 中实现相关接口。

获取当前时间

mbedtls 中有部分模块依赖于当前时间,比如证书解析中的有效期检测,用户需要使用到该部分功能则需要提供正确的获取当前时间的接口,可以通过定义MBEDTLS_PLATFORM_TIME_MACRO 来添加当前时间相关接口。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifdef MBEDTLS_HAVE_TIME

#define MBEDTLS_PLATFORM_TIME_MACRO xx_current_time

#include <**.h> // 硬件接口头文件

time_t xx_current_time(time_t * parm)
{
time_t time;

...... // 用户代码

return time;
}

#endif */ MBEDTLS_HAVE_TIME */

如果用户不想使用 time_t 时间结构体,可以通过定义 MBEDTLS_PLATFORM_TIME_TYPE_MACRO 定义时间件结构体。

适配熵源模块

在密码学中随机数是最基础的也是最重要的部分,mbedtls 中的熵池属于随机数模块部分,在 Linux 下可以使用 /dev/urandom 来提供熵源,在新的平台需要用户提供类似的熵源采集接口。可以通过定义 MBEDTLS_ENTROPY_HARDWARE_ALT 添加熵源采集接口,示例如下:

1
2
3
4
5
6
7
8
9
10
#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT

#include <**.h> // 硬件接口头文件

int mbedtls_hardware_poll( void *Data, unsigned char *Output, size_t Len, size_t *oLen )
{
...... // 用户代码
}

#endif /* MBEDTLS_ENTROPY_HARDWARE_ALT */

如果平台无法提供 /dev/urandomWindows CryptoAPI 的支持,则需要增加 MBEDTLS_NO_PLATFORM_ENTROPY 定义。

适配硬件加速

如果硬件平台支持加密算法,用户可以通过定义 MBEDTLS_*_ALT 来添加自己的硬件加密接口,比如 MBEDTLS_AES_ALT 可以用来替换 AES 加密相关接口,具体替换方法这里不做展开。

适配打印模块

mbedtls 调试打印接口使用的是标准库函数 printf(),用户如果想要将其替换为自己的接口,可以定义 MBEDTLS_PLATFORM_PRINTF_MACRO 来添加自己的打印接口。也可以通过定义 MBEDTLS_PLATFORM_PRINTF_ALT 然后调用 mbedtls_platform_set_printf() 接口设置自己的打印接口。

stm32 中可以将 printf 重定向到串口,这样可以不做任何修改。

mbedtls 移植总结

mbedtls 移植到新的硬件平台非常容易,用户可以在不修改 mbedtls 源文件的情况下完成移植工作,通过添加和修改宏定义的方式来增加和修改用户接口。此外 mbedtls 的模块化设计也使得用户在使用时可以选择性编译,可以很好的控制代码大小来节省硬件资源。

参考

0%