最近有客戶咨詢,希望能夠在剛完成設(shè)計的Apollo3產(chǎn)品上增強(qiáng)加密功能,主要是為了防止破解者非法克隆產(chǎn)品??蛻敉瑫r又提到Apollo3芯片資料上有寫到支持AES-128硬件加密模塊,還有唯一UID號碼,看看能不能把這兩塊利用起來,做“一機(jī)一密”的硬件保護(hù)。UID好處理,Apollo3將64位的全球唯一ID號(Unique Chip ID)存放在固定的地址上,直接讀取就好了。然而,Apollo3芯片手冊上僅僅介紹了Apollo3支持硬件AES-128硬件加密特性,但再也找不到更多相關(guān)的信息,官方提供的SDK里面也沒有AES的任何實(shí)現(xiàn)代碼。既然客戶提出了需求,我們就要想辦法實(shí)現(xiàn)。芯片自帶的硬件AES暫時無從下手,那么我們先來看看軟件AES算法如何?
首先,我們還是先來了解一下什么是AES吧
AES,全稱是The Advanced Encryption Standard,翻譯成中文是:高級加密標(biāo)準(zhǔn),但我們一般還是習(xí)慣讀作AES。AES是一種對稱密鑰算法,用于數(shù)據(jù)的加密和解密。在密碼學(xué)中AES又稱作Rijndael加密算法,是美國聯(lián)邦政府采用的一種區(qū)塊加密標(biāo)準(zhǔn)。AES是由美國國家研究院標(biāo)準(zhǔn)與技術(shù)(NIST)從2001年開始建立的一套數(shù)字加密標(biāo)準(zhǔn),發(fā)布于FIPS PUB 197,并在2002年5月26日成為有效的標(biāo)準(zhǔn)。2006年,高級加密標(biāo)準(zhǔn)已然成為對稱密鑰加密中最流行的算法之一。AES取代了原先的數(shù)據(jù)加密標(biāo)準(zhǔn)(DES),已經(jīng)被多方分析且廣為全世界所使用。AES使用128位固定大小的數(shù)據(jù)塊作為分組數(shù)據(jù)加密和解密數(shù)據(jù),即明文分組的長度固定為128位或16字節(jié)。AES可以使用128、192 和 256 位密鑰,根據(jù)密鑰長度不同,AES可分為AES-128,AES-192和AES-256。
AES加解密算法是基于置換和代替的。置換是指數(shù)據(jù)的重新排列,而代替是用一個單元數(shù)據(jù)替換另一個。AES使用了如下幾種不同的技術(shù)來實(shí)現(xiàn)置換和替換。
通過非線性的替換函數(shù),用查找表的方式把分組的字節(jié)矩陣中的每個字節(jié)用同一個S-BOX替換成另外一個字節(jié)。
行移位(ShiftRows):
一個簡單的位置交換。將矩陣中的每個橫列進(jìn)行循環(huán)式移位。
列混淆(MixColumns):
列混淆其實(shí)就是對一個狀態(tài)的每一列去乘一個矩陣,其中乘法是在有限域GF(2^8)內(nèi)進(jìn)行的,不可約多項式為x^8+x^4+x^3+x+1。如下圖1所示。為了充分混合矩陣中各個直行的操作。這個步驟使用線性轉(zhuǎn)換來混合每列的四個字節(jié)。
圖1列混淆(MixColumns)
注意,最后一個加密循環(huán)中省略MixColumns步驟,而以另一個AddRoundKey取代。
輪密鑰加(AddRoundKey):
當(dāng)前分組矩陣中的每一個字節(jié)和該次輪密鑰(round key)進(jìn)行按位異或運(yùn)算。輪密鑰是通過Key Schedule過程從密碼密鑰中得到的,輪密鑰長度等于分組長度。
密鑰的長度不同,加密輪數(shù)也不同,如下圖2所示:
圖2 不同長度密鑰的AES加密輪數(shù)
如下圖3所示是AES-128的加密的流程:
圖3 AES-128的加密的流程示意圖
對AES算法原理及實(shí)現(xiàn)過程感興趣的同學(xué),可以上網(wǎng)搜索更多關(guān)于AES的資料,這里不再詳述算法實(shí)現(xiàn)流程,重點(diǎn)討論怎么把AES這套算法在Apollo3 MCU上跑起來。
要實(shí)現(xiàn)AES算法,我了解到mbedTLS有成熟的AES加密算法庫支持,那么讓我們再來看看什么是mbedTLS吧。
mbedTLS,前身是PolarSSL,不管是arm掏錢買的,還是人家PolarSSL大方送的,總之,PolarSSL現(xiàn)在已經(jīng)屬于arm的資產(chǎn)了,而且arm官宣PolarSSL是arm的一部分了,還給改了個洋氣的名字叫mbedTLS。官方網(wǎng)頁置頂處赫然寫著醒目的標(biāo)題:PolarSSL is now part of arm Official announcement and rebranded as Mbed TLS。arm mbedTLS官方網(wǎng)站鏈接是:https://tls.mbed.org/。如下圖4。
圖4 mbedTLS官方網(wǎng)站
mbedTLS前世今生了解清楚了,我們還是回到正題,看看mbedTLS是什么,有哪些功能。
mbedTLS是TLS和SSL協(xié)議的實(shí)現(xiàn),并且需要相應(yīng)的加密算法和支持代碼。mbed TLS在Apache2.0許可證或GPL2.0許可證下作為開放源碼提供。Apache2.0許可證允許您在開放源碼和封閉源碼項目中使用mbed TLS。寬松的開源許可證,說白了,就是鼓勵大家用起來,用開來(當(dāng)然,最好是希望你用在arm自家的內(nèi)核MCU上。^_^)。網(wǎng)站宣傳mbed TLS的兩大特點(diǎn):Easy to use and Easy to get(容易使用,容易獲得)。
mbedTLS核心SSL 庫代碼是使用完全符合ANSI-C和MISRA-C的C語言編寫。mbedTLS實(shí)現(xiàn)了SSL模塊,基本加密功能并提供各種實(shí)用功能,如大家常見的AES, DES, RSA, SHA,MD5等都已經(jīng)完整實(shí)現(xiàn)了。與OpenSSL和其他TLS的實(shí)現(xiàn)不同,mbed TLS的設(shè)計目標(biāo)是作為適合小型嵌入式設(shè)備來使用的,其最小的完整的TLS堆棧只需要60KB的Flash程序空間和64KB的RAM。它也是高度模塊化的:每個組件,如加密函數(shù),是可以獨(dú)立于框架的其余部分使用。
介紹完AES及mbedTLS的基本概念之后,下面我們就開始動手干活了。
客戶使用的IDE是KEIL MDK,假設(shè)我們已經(jīng)打開了Apollo3 SDK里面自帶的演示例程或者客戶自己的項目工程,我們就開始一步一步詳細(xì)介紹在Apollo3平臺上如何實(shí)現(xiàn)AES加解密算法。
1、安裝mbedTLS Pack
點(diǎn)擊KEIL MDK菜單上的Pack Installer圖標(biāo),如下圖5截圖紅框所示,加載Pack Installer,界面如下圖6所示。
圖5 KEIL MDK界面上的Pack Installer圖標(biāo)
圖6Pack Installer界面
在設(shè)備廠家ARM目錄下找到ARM::mbedTLS,圖6顯示我已經(jīng)安裝好了最新的v1.6.0版本;如果沒有安裝,點(diǎn)擊Install安裝,或者點(diǎn)擊Update將版本升級到最新的v1.6.0版本。
2、加載mbedTLS組件到我們的工程中
點(diǎn)擊KEIL MDK菜單上的Manage Run-Time Environment圖標(biāo),如下圖7截圖紅框所示,加載Manage Run-Time Environment,界面如下圖8所示。
圖7 KEIL MDK界面上Manage Run-Time
Environment圖標(biāo)
圖8Manage Run-Time Environment界面
在Manage Run-Time Environment的這個界面上面找到Security,勾選mbedTLS,點(diǎn)擊OK按鈕退出。
這個時候,我們在KEIL MDK項目工程目錄下就能看到多了一個Security組件及其源代碼列表,如下圖9所示。
圖9 添加Security組件到項目工程中
3、添加頭文件到代碼里,并修改mbedTLS_config.h文件
在需調(diào)用mbedTLS的AES API的代碼里添加如下頭文件:
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/aes.h"
根據(jù)自己需要使用到的功能,修改mbedTLS_config.h文件,比如我這里需要用到AES,那么就需要打開相應(yīng)的宏定義就好了。對于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加密解密相關(guān)API介紹
根據(jù)需要我們大致經(jīng)常會用到AES的兩種加密模式ECB和CBC。
ECB:就是把數(shù)據(jù)塊進(jìn)行加密,每16字節(jié)為一塊,依次進(jìn)行加密,直到完成,長度不足的補(bǔ)0。
CBC:cipher block chaining,是一種循環(huán)模式,前一個分組的密文和當(dāng)前分組的明文異或后再加密,這樣做的目的也是為了增強(qiáng)破解難度。
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 );
聲明一個結(jié)構(gòu)體類型,成員分別存放AES的參數(shù),如加密輪數(shù),輪密鑰指針,生成輪密鑰的緩沖區(qū)等。
/**
* \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;
結(jié)構(gòu)體初始化函數(shù):
/**
* \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;
// 初始化結(jié)構(gòu)體
mbedtls_aes_init( &aes_ctx );
// 設(shè)置解密密鑰
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;
// 初始化結(jié)構(gòu)體
mbedtls_aes_init( &aes_ctx );
// 設(shè)置解密密鑰
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上實(shí)現(xiàn)AES算法的步驟已經(jīng)全部完成。
看看,基于mbedTLS在Apollo3 MCU上實(shí)現(xiàn)MCU算法是不是特別簡單、易用?那么回到客戶的問題,我們?nèi)绾卫眯酒ㄒ籌D結(jié)合AES加密算法來做點(diǎn)加密呢。
1、 讀取芯片UID
通過閱讀Apollo3 MCU芯片Datasheet,我們知道64位全球唯一ID號(Unique Chip ID)連續(xù)存放在固定的地址上,起始地址為:0x40020004。這個唯一ID號每片芯片都不同的。如下圖10所示。
圖10 Apollo3 芯片Unique Chip ID
讀取UID的代碼很簡單:
uint32_t uid0, uid1;
uid0 = (*((uint32_t *)0x40020004));
uid1 = (*((uint32_t *)0x40020008));
2、 利用芯片UID構(gòu)造AES明文序列和密鑰
我們把uid0和uid1做異或得到一個常數(shù),利用這個常數(shù)來構(gòu)造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加密生成產(chǎn)品特征碼
最后,對上述生成的明文序列_aPlaintext[256]和密鑰_aKey[32]執(zhí)行AES-256 CBC加密算法,并將密文結(jié)果_aCipher[256]做CRC32運(yùn)算,參考代碼如下:
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字節(jié)的結(jié)果我們將其看作是產(chǎn)品的唯一特征碼,真正做到一機(jī)一密,可以用它來驗證原裝產(chǎn)品,也可以防止非法破解和克隆產(chǎn)品。我們可以將這個特征碼存放到用戶指定的Flash區(qū)域或者INFO里面,上電后根據(jù)上述流程做AES加密算法后,結(jié)果與存儲在芯片的特征碼做比對,只有原裝產(chǎn)品才允許運(yùn)行代碼,有效杜絕產(chǎn)品被非法克隆的風(fēng)險。
在其他IDE環(huán)境下加載并使用mbedTLS組件
如果說你偏好的IDE不是Keil MDK,那么也沒有關(guān)系,畢竟mbedTLS的源代碼還是非常容易獲取的,你可以從arm/TLS官網(wǎng):https://tls.mbed.org/直接下載,點(diǎn)擊下面的圖標(biāo)也可以直接下載哦。
另外,還有一種方法。
arm官方Github賬號上Git完整源代碼:git clone https://github.com/ARMmbed/mbedtls.git,當(dāng)然,以后你也可以不定期地git pull,申請到最新的版本更新,下載和更新都非常的方便。將mbedTLS源碼(library文件夾下)全部添加到你的工程里面,并在你要調(diào)用mbedTLS API的代碼里面添加相應(yīng)的頭文件,配置一下mbedTLS_config.h頭文件里面的宏定義就可以正常使用了。mbedTLS API的調(diào)用方法和Keil MDK里面完全一樣,畢竟mbedTLS是用純C語言編寫的,不存在移植的問題。
后記
也許大家還想知道添加mbedTLS AES加密算法到項目里面會占用多少Flash和SRAM空間。我們拿數(shù)據(jù)說話吧,經(jīng)過對比加入AES前后產(chǎn)生的map文件得知,項目中加入AES-256 CBC加解密算法代碼的部分占用Flash 11.63KB, SRAM 0KB(armcc編譯器優(yōu)化 Level0(-O0)),對MCU的存儲資源占用較少,對整個系統(tǒng)的影響很小。
再來看看AES-256 CBC的實(shí)際運(yùn)行效率,我們利用ARM內(nèi)核自帶的Systick Timer,對AES加密操作進(jìn)行計時,看看到底消耗多少時間。參考代碼如下:
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就產(chǎn)生了溢出,需要RELOAD
// DURATION越小說明程序執(zhí)行時間越短,效率越高!
printf("DURATION of process of mbedtls_aes_cbc_test() : 0x%.8X.\n\r", DURATION);
__set_PRIMASK(old_primask);
從實(shí)驗數(shù)據(jù)得知,運(yùn)行一次明文長度為256字節(jié)的AES-256 CBC加密算法耗時僅需1.2ms(Apollo3 running @ 48MHz),這對于系統(tǒng)來說影響也很小。
-
mbedTLS
+關(guān)注
關(guān)注
0文章
6瀏覽量
580
發(fā)布評論請先 登錄
相關(guān)推薦
評論