基于mbedTLS在Apollo3 MCU上实现AES-256加解密算法

上海润欣科技股份有限公司创研社
最近有客户咨询,希望能够在刚完成设计的apollo3产品上增强加密功能,主要是为了防止破解者非法克隆产品。客户同时又提到apollo3芯片资料上有写到支持aes-128硬件加密模块,还有唯一uid号码,看看能不能把这两块利用起来,做“一机一密”的硬件保护。uid好处理,apollo3将64位的全球唯一id号(unique chip id)存放在固定的地址上,直接读取就好了。然而,apollo3芯片手册上仅仅介绍了apollo3支持硬件aes-128硬件加密特性,但再也找不到更多相关的信息,官方提供的sdk里面也没有aes的任何实现代码。既然客户提出了需求,我们就要想办法实现。芯片自带的硬件aes暂时无从下手,那么我们先来看看软件aes算法如何?
首先,我们还是先来了解一下什么是aes吧
aes,全称是the advanced encryption standard,翻译成中文是:高级加密标准,但我们一般还是习惯读作aes。aes是一种对称密钥算法,用于数据的加密和解密。在密码学中aes又称作rijndael加密算法,是美国联邦政府采用的一种区块加密标准。aes是由美国国家研究院标准与技术(nist)从2001年开始建立的一套数字加密标准,发布于fips pub 197,并在2002年5月26日成为有效的标准。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。aes取代了原先的数据加密标准(des),已经被多方分析且广为全世界所使用。aes使用128位固定大小的数据块作为分组数据加密和解密数据,即明文分组的长度固定为128位或16字节。aes可以使用128、192 和 256 位密钥,根据密钥长度不同,aes可分为aes-128,aes-192和aes-256。
aes加解密算法是基于置换和代替的。置换是指数据的重新排列,而代替是用一个单元数据替换另一个。aes使用了如下几种不同的技术来实现置换和替换。
字节替代(subbytes):
通过非线性的替换函数,用查找表的方式把分组的字节矩阵中的每个字节用同一个s-box替换成另外一个字节。
行移位(shiftrows):
一个简单的位置交换。将矩阵中的每个横列进行循环式移位。
列混淆(mixcolumns):
列混淆其实就是对一个状态的每一列去乘一个矩阵,其中乘法是在有限域gf(2^8)内进行的,不可约多项式为x^8+x^4+x^3+x+1。如下图1所示。为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每列的四个字节。
图1列混淆(mixcolumns)
注意,最后一个加密循环中省略mixcolumns步骤,而以另一个addroundkey取代。
轮密钥加(addroundkey):
当前分组矩阵中的每一个字节和该次轮密钥(round key)进行按位异或运算。轮密钥是通过key schedule过程从密码密钥中得到的,轮密钥长度等于分组长度。
密钥的长度不同,加密轮数也不同,如下图2所示:
图2 不同长度密钥的aes加密轮数
如下图3所示是aes-128的加密的流程:
图3 aes-128的加密的流程示意图
对aes算法原理及实现过程感兴趣的同学,可以上网搜索更多关于aes的资料,这里不再详述算法实现流程,重点讨论怎么把aes这套算法在apollo3 mcu上跑起来。
要实现aes算法,我了解到mbedtls有成熟的aes加密算法库支持,那么让我们再来看看什么是mbedtls吧。
mbedtls,前身是polarssl,不管是arm掏钱买的,还是人家polarssl大方送的,总之,polarssl现在已经属于arm的资产了,而且arm官宣polarssl是arm的一部分了,还给改了个洋气的名字叫mbedtls。官方网页置顶处赫然写着醒目的标题:polarssl is now part of arm official announcement and rebranded as mbed tls。arm mbedtls官方网站链接是:https://tls.mbed.org/。如下图4。
图4 mbedtls官方网站
mbedtls前世今生了解清楚了,我们还是回到正题,看看mbedtls是什么,有哪些功能。
mbedtls是tls和ssl协议的实现,并且需要相应的加密算法和支持代码。mbed tls在apache2.0许可证或gpl2.0许可证下作为开放源码提供。apache2.0许可证允许您在开放源码和封闭源码项目中使用mbed tls。宽松的开源许可证,说白了,就是鼓励大家用起来,用开来(当然,最好是希望你用在arm自家的内核mcu上。^_^)。网站宣传mbed tls的两大特点:easy to use and easy to get(容易使用,容易获得)。
mbedtls核心ssl 库代码是使用完全符合ansi-c和misra-c的c语言编写。mbedtls实现了ssl模块,基本加密功能并提供各种实用功能,如大家常见的aes, des, rsa, sha,md5等都已经完整实现了。与openssl和其他tls的实现不同,mbed tls的设计目标是作为适合小型嵌入式设备来使用的,其最小的完整的tls堆栈只需要60kb的flash程序空间和64kb的ram。它也是高度模块化的:每个组件,如加密函数,是可以独立于框架的其余部分使用。
介绍完aes及mbedtls的基本概念之后,下面我们就开始动手干活了。
客户使用的ide是keil mdk,假设我们已经打开了apollo3 sdk里面自带的演示例程或者客户自己的项目工程,我们就开始一步一步详细介绍在apollo3平台上如何实现aes加解密算法。
1、安装mbedtls pack
点击keil mdk菜单上的pack installer图标,如下图5截图红框所示,加载pack installer,界面如下图6所示。
图5 keil mdk界面上的pack installer图标
图6 pack installer界面
在设备厂家arm目录下找到arm::mbedtls,图6显示我已经安装好了最新的v1.6.0版本;如果没有安装,点击install安装,或者点击update将版本升级到最新的v1.6.0版本。
2、加载mbedtls组件到我们的工程中
点击keil mdk菜单上的manage run-time environment图标,如下图7截图红框所示,加载manage run-time environment,界面如下图8所示。
图7 keil mdk界面上manage run-time
environment图标
图8 manage run-time environment界面
在manage run-time environment的这个界面上面找到security,勾选mbedtls,点击ok按钮退出。
这个时候,我们在keil mdk项目工程目录下就能看到多了一个security组件及其源代码列表,如下图9所示。
图9 添加security组件到项目工程中
3、添加头文件到代码里,并修改mbedtls_config.h文件
在需调用mbedtls的aes api的代码里添加如下头文件:
#include mbedtls/entropy.h
#include mbedtls/ctr_drbg.h
#include mbedtls/aes.h
根据自己需要使用到的功能,修改mbedtls_config.h文件,比如我这里需要用到aes,那么就需要打开相应的宏定义就好了。对于aes的ecb和cbc加解密,打开如下的这几个宏定义:
#define mbedtls_aes_rom_tables
#define mbedtls_cipher_mode_cbc
#define mbedtls_aes_c
#define mbedtls_cipher_padding_pkcs7
#define mbedtls_no_platform_entropy
#define mbedtls_ctr_drbg_c
#define mbedtls_entropy_c
#define mbedtls_sha256_c
4、aes加密解密相关api介绍
根据需要我们大致经常会用到aes的两种加密模式ecb和cbc。
ecb:就是把数据块进行加密,每16字节为一块,依次进行加密,直到完成,长度不足的补0。
cbc:cipher block chaining,是一种循环模式,前一个分组的密文和当前分组的明文异或后再加密,这样做的目的也是为了增强破解难度。
aes-ecb加解密算法api:
/**
 * \brief  this function performs an aes single-block encryption or
 *    decryption operation.
 *
 *  it performs the operation defined in the \p mode parameter
 *   (encrypt or decrypt), on the input data buffer defined in
 *  the \p input parameter.
 *
*mbedtls_aes_init(), and either mbedtls_aes_setkey_enc() or
 *  mbedtls_aes_setkey_dec() must be called before the first
 *                 call to this api with the same context.
 *
 * \param ctx      the aes context to use for encryption or decryption.
*                 it must be initialized and bound to a key.
* \param mode     the aes operation: #mbedtls_aes_encrypt or
 *                 #mbedtls_aes_decrypt.
 * \param input    the buffer holding the input data.
 *                 it must be readable and at least \c 16 bytes long.
 * \param output   the buffer where the output data will be written.
 *                 it must be writeable and at least \c 16 bytes long.
* \return         \c 0 on success.
 */
int mbedtls_aes_crypt_ecb( mbedtls_aes_context *ctx,
                    int mode,
                    const unsigned char input[16],
                    unsigned char output[16] );
aes-cbc加解密算法api:
/**
 * \brief  this function performs an aes-cbc encryption or decryption operation
 *         on full blocks.
 *
 *         it performs the operation defined in the \p mode
 *         parameter (encrypt/decrypt), on the input data buffer defined in
 *         the \p input parameter.
 *
 *         it can be called as many times as needed, until all the input
 *         data is processed. mbedtls_aes_init(), and either
 *         mbedtls_aes_setkey_enc() or mbedtls_aes_setkey_dec() must be called
 *         before the first call to this api with the same context.
 *
 * \note   this function operates on full blocks, that is, the input size
 *         must be a multiple of the aes block size of \c 16 bytes.
 *
 * \note   upon exit, the content of the iv is updated so that you can
 *         call the same function again on the next
 *         block(s) of data and get the same result as if it was
 *         encrypted in one call. this allows a streaming usage.
 *         if you need to retain the contents of the iv, you should
 *         either save it manually or use the cipher module instead.
 *
 *
 * \param ctx      the aes context to use for encryption or decryption.
 *                 it must be initialized and bound to a key.
 * \param mode     the aes operation: #mbedtls_aes_encrypt or
 *                 #mbedtls_aes_decrypt.
 * \param length   the length of the input data in bytes. this must be a
 *                 multiple of the block size (\c 16 bytes).
 * \param iv       initialization vector (updated after use).
 *                 it must be a readable and writeable buffer of \c 16 bytes.
 * \param input    the buffer holding the input data.
 *                 it must be readable and of size \p length bytes.
 * \param output   the buffer holding the output data.
 *                 it must be writeable and of size \p length bytes.
 *
 * \return         \c 0 on success.
 * \return         #mbedtls_err_aes_invalid_input_length
 *                 on failure.
 */
int mbedtls_aes_crypt_cbc( mbedtls_aes_context *ctx,
                    int mode,
                    size_t length,
                    unsigned char iv[16],
                    const unsigned char *input,
                    unsigned char *output );
声明一个结构体类型,成员分别存放aes的参数,如加密轮数,轮密钥指针,生成轮密钥的缓冲区等。
/**
 * \brief the aes context-type definition.
 */
typedef struct mbedtls_aes_context
{
    int nr;                     /*!< the number of rounds. */
    uint32_t *rk;               /*!< aes round keys. */
    uint32_t buf[68];           /*!< unaligned data buffer. this buffer can
                                     hold 32 extra bytes, which can be used for
                                     one of the following purposes:
alignment if via padlock is
used.
simplifying key expansion in the 256-bit
case by generating an extra round key.
                                        */
}mbedtls_aes_context;
结构体初始化函数:
/**
 * \brief          this function initializes the specified aes context.
 *
 *                 it must be the first api called before using
 *                 the context.
 *
 * \param ctx      the aes context to initialize. this must not be \c null.
 */
void mbedtls_aes_init( mbedtls_aes_context *ctx );
aes-128 cbc加密参考代码:
mbedtls_aes_context aes_ctx;
// 初始化结构体
mbedtls_aes_init( &aes_ctx );
// 设置解密密钥
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
// aes-128 cbc加密
mbedtls_aes_crypt_cbc(&aes_ctx, mbedtls_aes_encrypt, 64, iv, plain, cipher);
aes-128 cbc解密参考代码:
mbedtls_aes_context aes_ctx;
// 初始化结构体
mbedtls_aes_init( &aes_ctx );
// 设置解密密钥
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
// aes-128 cbc解密
mbedtls_aes_crypt_cbc(&aes_ctx, mbedtls_aes_decrypt, 64, iv, cipher, dec_plain);
至此,在apollo3 mcu上实现aes算法的步骤已经全部完成。
看看,基于mbedtls在apollo3 mcu上实现mcu算法是不是特别简单、易用?那么回到客户的问题,我们如何利用芯片唯一id结合aes加密算法来做点加密呢。
1、 读取芯片uid
通过阅读apollo3 mcu芯片datasheet,我们知道64位全球唯一id号(unique chip id)连续存放在固定的地址上,起始地址为:0x40020004。这个唯一id号每片芯片都不同的。如下图10所示。
图10 apollo3 芯片unique chip id
读取uid的代码很简单:
uint32_t uid0, uid1;
uid0 = (*((uint32_t *)0x40020004));
uid1 = (*((uint32_t *)0x40020008));
2、 利用芯片uid构造aes明文序列和密钥
我们把uid0和uid1做异或得到一个常数,利用这个常数来构造aes加密的明文序列和密钥。参考代码如下:
uint32_t i;
    uint32_t seed;
    unsigned int temp; 
    unsigned char _akey[32];
    unsigned char _aplaintext[256];
    unsigned char _acipher[256]={0};
    unsigned char _aiv[16] = {0};
seed = uid0 ^ uid1;
// _aplaintext[256]
    i = 0;
    do
    {
        _aplaintext[i] = (unsigned char)((seed + i)^0x5a5a5a5a5a);
        ++i;
    }while ( i > i;
        *(_akey+i) = (unsigned char)(temp ^ 0xa5a5a5a5a5);
        i++;
    }while ( i ctrl = 0; // disable systick
systick->load = 0xffffff; // count down from maximum value
systick->val = 0; // clear current value to 0
systick->ctrl = 0x5; // enable systick, and use processor clock
while (systick->val == 0); // wait until systick reloaded
start_time = systick->val; // read start time value
mbedtls_aes256_cbc_test();
stop_time = systick->val; // read stop time value
systick->ctrl = 0; // disable systick
if ((systick->ctrl & 0x10000) == 0) // if no overflow
duration = start_time - stop_time; // calculate total cycles
    else
        printf (timer overflowed\n);
// systick->val递减,减到0就产生了溢出,需要reload
// duration越小说明程序执行时间越短,效率越高!
printf(duration of process of mbedtls_aes_cbc_test() : 0x%.8x.\n\r, duration);
__set_primask(old_primask);
从实验数据得知,运行一次明文长度为256字节的aes-256 cbc加密算法耗时仅需1.2ms(apollo3 running @ 48mhz),这对于系统来说影响也很小。

2018年工控行业的十大前沿技术资料概述
简述太阳能光伏组件应用领域
隔离技术的特性与在传感器中的作用
苹果一直没有放弃造车业务,持续挖角扩充人才
LED照明助WiFi速度提升10倍?
基于mbedTLS在Apollo3 MCU上实现AES-256加解密算法
多款固态硬盘对比谁最好
麒麟990和麒麟9000处理器哪个好
无人机都怕哪些干扰,一起来看看吧
HMD似乎还留了一手,诺基亚二季度发布一款新机
SKYLAB:介绍5款支持一主多从的BLE蓝牙模块
EPS Global推出安全IC烧录服务,以符合国际安全法规并避免网络攻击
常见的除静电设备有哪些?
韩国抢跑5G不但价格高并且覆盖不全面
磁翻板液位计翻片乱翻的原因及解决办法
惠瑞捷同时推出可扩展测试机台与数字通道卡
若英国脱欧影响供应链,宝马将关闭英国工厂
电子集成全面解析:7种(5+2)集成技术
人工智能行业的七大发展趋势
咪咕正在全力探索5G技术为5G商用时代大直播格局全力备战