0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

润欣科技Fortune 2019-12-06 21:57 次阅读

上海润欣科技股份有限公司创研社


最近有客户咨询,希望能够在刚完成设计的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所示。为了充分混合矩阵中各个直行的操作。这个步骤使用线性转换来混合每列的四个字节。


1575600432732133.png

图1列混淆(MixColumns)


注意,最后一个加密循环中省略MixColumns步骤,而以另一个AddRoundKey取代。


轮密钥加(AddRoundKey):


当前分组矩阵中的每一个字节和该次轮密钥(round key)进行按位异或运算。轮密钥是通过Key Schedule过程从密码密钥中得到的,轮密钥长度等于分组长度。

密钥的长度不同,加密轮数也不同,如下图2所示:


1575600443467887.png

图2 不同长度密钥的AES加密轮数


如下图3所示是AES-128的加密的流程:


1575600466378487.png

图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。


1575600482488850.png

图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所示。


1575600517256841.png

图5 KEIL MDK界面上的Pack Installer图标


1575600542213712.png

图6Pack 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所示。


1575600570580613.png

图7 KEIL MDK界面上Manage Run-Time

Environment图标


1575600581792035.png

图8Manage Run-Time Environment界面


Manage Run-Time Environment的这个界面上面找到Security,勾选mbedTLS,点击OK按钮退出。


这个时候,我们在KEIL MDK项目工程目录下就能看到多了一个Security组件及其源代码列表,如下图9所示。


1575600594817295.png

图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所示。



1575600658526715.png

图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 < 256 );                          


// _aKey[32]

i = 0;

do

{

temp = seed >> i;

*(_aKey+i) = (unsigned char)(temp ^ 0xA5A5A5A5A5);

i++;

}while ( i < 32 );


3、 AES加密生成产品特征码


最后,对上述生成的明文序列_aPlaintext[256]和密钥_aKey[32]执行AES-256 CBC加密算法,并将密文结果_aCipher[256]做CRC32运算,参考代码如下:


mbedtls_aes_init( &aes_ctx );

mbedtls_aes_setkey_enc(&aes_ctx, _aKey, 256);

mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, 256, _aIV, _aPlaintext, _aCipher);

return CalcCRC32(_aCipher, 256, 0);


最终得到4字节的结果我们将其看作是产品的唯一特征码,真正做到一机一密,可以用它来验证原装产品,也可以防止非法破解和克隆产品。我们可以将这个特征码存放到用户指定的Flash区域或者INFO里面,上电后根据上述流程做AES加密算法后,结果与存储在芯片的特征码做比对,只有原装产品才允许运行代码,有效杜绝产品被非法克隆的风险。



在其他IDE环境下加载并使用mbedTLS组件


如果说你偏好的IDE不是Keil MDK,那么也没有关系,毕竟mbedTLS的源代码还是非常容易获取的,你可以从arm/TLS官网:https://tls.mbed.org/直接下载,点击下面的图标也可以直接下载哦。


另外,还有一种方法。


arm官方Github账号上Git完整源代码:git clone https://github.com/ARMmbed/mbedtls.git,当然,以后你也可以不定期地git pull,申请到最新的版本更新,下载和更新都非常的方便。将mbedTLS源码(library文件夹下)全部添加到你的工程里面,并在你要调用mbedTLS API的代码里面添加相应的头文件,配置一下mbedTLS_config.h头文件里面的宏定义就可以正常使用了。mbedTLS API的调用方法和Keil MDK里面完全一样,毕竟mbedTLS是用纯C语言编写的,不存在移植的问题。


后记


也许大家还想知道添加mbedTLS AES加密算法到项目里面会占用多少Flash和SRAM空间。我们拿数据说话吧,经过对比加入AES前后产生的map文件得知,项目中加入AES-256 CBC加解密算法代码的部分占用Flash 11.63KB, SRAM 0KB(armcc编译器优化 Level0(-O0)),对MCU的存储资源占用较少,对整个系统的影响很小。


再来看看AES-256 CBC的实际运行效率,我们利用ARM内核自带的Systick Timer,对AES加密操作进行计时,看看到底消耗多少时间。参考代码如下:


unsigned int old_primask = __get_PRIMASK();

__disable_irq();

SysTick->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),这对于系统来说影响也很小。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mbedTLS
    +关注

    关注

    0

    文章

    6

    浏览量

    574
收藏 人收藏

    评论

    相关推荐

    aes算法移动应用中的应用场景

    传输敏感数据,如个人信息、账户密码、交易记录等。AES算法能够对这些数据进行加密,确保传输过程中不被窃取或篡改。例如,移动支付应用中,AES
    的头像 发表于 11-14 15:14 211次阅读

    aes加密的常见错误及解决方案

    的归纳以及相应的解决方案: 常见错误 编码问题 : 将字节数组转换成字符串时,如果使用了不同的编码格式,可能会导致解密后的数据出现乱码。 密钥长度问题 : AES算法支持128位、1
    的头像 发表于 11-14 15:13 394次阅读

    aes云存储中的应用实例

    ,使用相同的密钥进行加密和解密。它支持128、192和256位的密钥长度,具有很高的安全性。AES加密过程包括多个轮次,每一轮包括四个步骤:SubBytes(字节替换)、ShiftRows(行移位)、MixColumns(列混淆
    的头像 发表于 11-14 15:11 241次阅读

    aes算法在数据传输中的应用

    AES算法由美国国家标准与技术研究院(NIST)于2001年正式采纳为加密标准,它是一种基于块的加密算法,使用128位、192位或256位的密钥长度,对数据块进行加密。
    的头像 发表于 11-14 15:09 237次阅读

    4G模组加解密艺术:通用函数的深度解读

    今天是对加解密通用函数的深度解读,我将详细讲解,建议收藏,不可错过。
    的头像 发表于 11-12 09:58 159次阅读
    4G模组<b class='flag-5'>加解密</b>艺术:通用函数的深度解读

    蓝牙AES+RNG如何保障物联网信息安全

    标准,主要用于保护电子数据的安全。AES 支持128、192、和256位密钥长度,其中AES-128是最常用的一种,它使用128位(16字节)的密钥进行加密和解密操作。
    发表于 11-08 15:38

    SITARA MCU器件上集成MbedTLS

    电子发烧友网站提供《SITARA MCU器件上集成MbedTLS.pdf》资料免费下载
    发表于 08-27 10:44 0次下载
    <b class='flag-5'>在</b>SITARA <b class='flag-5'>MCU</b>器件上集成<b class='flag-5'>MbedTLS</b>

    使用mbedtls连接到aws iot,无法编译代码的原因?

    MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 /* * Save RAM at the expense
    发表于 07-12 07:24

    基于FPGA的AES256光纤加密设计

    AES算法的硬件实现,国内外研究学者大部分是基于FPGA的硬件实现。解放军理工大学的吕游等人研究高级加密标准俄歇算法的设计原理,并通过加密部
    发表于 06-19 19:50

    ESP32-S3 HTTPS esp_mbedtls_handshake握手平均耗时3s以上,有什么改善方法吗?

    当我使用HTTP POST/GET请求时,每次请求耗时一般100-300ms左右 更改为HTTPS,并加入服务器证书校验后,每次请求耗时需要3s以上 我尝试过下面这几种配置方式,但是耗时变化不大
    发表于 06-13 06:37

    鸿蒙OS开发问题:(ArkTS)【 RSA加解密,解决中文乱码等现象】

    RSA加解密开始构建工具类就是举步维艰,官方文档虽然很全,但是还是有很多小瑕疵,自己经过几天的时间,彻底解决了中文乱码的问题、分段加密的问题。
    的头像 发表于 03-27 21:23 1671次阅读
    鸿蒙OS开发问题:(ArkTS)【 RSA<b class='flag-5'>加解密</b>,解决中文乱码等现象】

    基于FPGA的可编程AES加解密IP

    可编程AES加解密IP内建密钥扩展功能,使用初始密钥产生扩展密钥,用于加解密过程。可编程AES加解密IP处理128-bit分组数据,并且支持
    发表于 01-09 10:49 433次阅读
    基于FPGA的可编程<b class='flag-5'>AES</b><b class='flag-5'>加解密</b>IP

    【先楫HPM5361EVK开发板试用体验】3手把手实战安全数据处理器 SDP

    AES-256的加密数据)。 那么SM4是什么呢?SM4是一种对称加密算法,也就是加密和解密使用的是同一把“钥匙”。它是中国自主研发的一种加密
    发表于 12-19 00:36

    21489可以启动加载程序过程中将SPIFLASH中的程序按照一定的解密算法解密后,加载到内部RAM运行吗?

    解密算法解密后,加载到内部RAM运行?这样,只要将编译好的程序经过一定的加密算法处理后,再烧写到SPIFLASH中,就算别人能够读出程序也没什么作用。有没有这种功能或者
    发表于 11-30 07:47

    eCryptfs整体架构和核心加解密机制介绍

    的注册函数。之后eCryptfs根据mountsession中保存的key signature参数从keyring中找到对应密钥(FEKEK),再调用crypto模块API完成文件加解密。 eCryptfs核心加解密
    的头像 发表于 11-29 11:43 817次阅读
    eCryptfs整体架构和核心<b class='flag-5'>加解密</b>机制介绍