KW36 AES-CMAC 时间比较

关键字 :KW36CMACnxp

目录

一、  问题描述

二、代码过程

2.1 LTC CMAC 描述

2.2 代码解析

三、结果

四、参考文档

一、 问题描述

     在工作过程中,需要用 CMAC 校验 Flash 里面的数据,并且时间还要尽可能的短,在这里就写一个参考的函数来使用 KW36 的 LTC 模块。此次代码针对大于 64 字节大小数据的 CMAC 计算。

二、代码过程

2.1 LTC CMAC 描述

      在 LTC 模块的 CMAC 中,状态总共分为 INITIALIZE、UPDATE 和 FINALIZE(其中INITIALIZE/ FINALIZE 状态不在此次范围,因为明显一次计算无法计算完整个 Flash )。

      在 INITIALIZE 过程中,先对 LTC 进行初始化,然后在 UPDATE 中写入数据,数据写入寄存器 IFIFO(IFIFO 最大是 4*4 Bytes,但注意一次性数据大小最大的 4095),最后 FINALIZE 获取 UPDATE 时的 MAC 和 L ,重写入上下文寄存器后计算出最后的 MAC。

2.2 代码解析

       由于要从 Flash 中读取数据,选择数据宽度为 32bits,则代码过程如下(这个函数直接算出了 CMAC,后面的函数皆为其调用):

/*
* 计算 CMAC
* @param[in] base : LTC 寄存器指针
* @param[in] input : 输入数据的指针
* @param[in] inputSize : 输入数据的大小(字为单位)
* @param[in] key : 密钥指针(16Bytes)
* @param[out] output : 输出 CMAC 值指针
* @out :
* */
status_t LTC_CMAC_Calculate(
LTC_Type *base,
uint32_t *input,
uint32_t inputSize,
uint32_t *key,
uint32_t *output){

status_t status = kStatus_Success;

// 循环中最终地址
uint32_t * cycle_final_addr=NULL;
// 循环次数
uint32_t cycle_count=0;
// 循环过程中一次 session 计算数据的大小(字)
uint32_t cycle_once_sesseion_count=0x3FC;//0xFF0/4;
// 没进入循环的参与数据大小(字)
uint32_t cycle_out_count=0;

// 临时存的 MAC 和 L
uint32_t mac_l_temp[8]={0,0,0,0};

uint8_t z=0;
uint8_t i=0;

/* 验证有 64 字节的数据 */
if(inputSize<4){
return kStatus_Fail;
}

// 清楚内部寄存器
base->CW=0xC000006D;
// 设置 SWAP
base->CTL=0x00F30000;
// 写入密钥
for(i=0;i<4;i++){
base->KEY[i]=key[i];
}
// 写入密钥长度值
base->KS=0x10;
// 清除 DONE 标志位
base->STA=0x00010000;
// 设置模式(初始、加密、AES、CMAC)
base->MD=0x00100605;
// 设置数据长度
base->DS=0x00000000;

// 等待结束
status=LTC_WAIT(base);
if(kStatus_Success!=status){
return status;
}

// 清楚数据大小寄存器
base->CW = (uint32_t)0x04;

// 设置模式(更新、非加密、AES、CMAC)
base->MD=0x00100600;

/************************** 第一次计算 ***************************/
// 计算循环次数
cycle_count=inputSize / cycle_once_sesseion_count;
// 没进入循环的参与数据大小(字)
cycle_out_count= inputSize-cycle_count*cycle_once_sesseion_count;
// 如果剩余的不足 16 字节,计算循环次数减去一,没进入的循环的数据大小为 0x3FC(字)(不足16字节的去掉了)
if(4>cycle_out_count){
cycle_count-=1;
cycle_out_count=cycle_once_sesseion_count;
}
// 循环中最终地址
cycle_final_addr=input+(cycle_count*cycle_once_sesseion_count);

// 计算循环数据的 CMAC
status=LTC_CMAC_Calculate_Cycle(base,input,cycle_final_addr,cycle_count);
// status=LTC_CMAC_Calculate_Cycle1(base,input,cycle_final_addr,4080);
if(kStatus_Success!=status ){
return status;
}

/************************** 第二次计算 ***************************/
/* 查看剩余的数据还有多少个 block 大小,更新剩余次数的 block 循环次数 */
cycle_count=cycle_out_count/4-1;
if(0!=cycle_count){
base->DS=cycle_count*16;
cycle_count*=4;
// 第一层循环,是剩余数据 包含多少 字
for(i=0;i<cycle_count;i++){
if(0U == (base->FIFOSTA & LTC_FIFOSTA_IFF_MASK)){
/* 复制数据到 FIFO */
base->IFIFO =*cycle_final_addr;
cycle_final_addr++;
z++;
}
}
// 等待结束
status=LTC_WAIT(base);
if(kStatus_Success!=status){
return status;
}
}
// 读出 MAC 、L
for(i=0;i<8;i++){
mac_l_temp[i]=base->CTX[i];
}
// 清除内部寄存器
base->CW=0xC000006D;
/************************** 第三次计算(最终) ***************************/
// 清楚数据大小寄存器和模式寄存器
base->CW = (uint32_t)0x05;
// 清除 DONE 标志位
base->STA=0x00010000;
// 清楚数据大小寄存器
base->CW = (uint32_t)0x04;
// 设置模式(结束、非加密、AES、CMAC)
base->MD=0x00100608;

// 写入 MAC 、L
for(i=0;i<8;i++){
base->CTX[i]=mac_l_temp[i];
}

// 清除 钥匙大小寄存器
base->CW=0x40;
// 写入密钥
for(i=0;i<4;i++){
base->KEY[i]=key[i];
}
// 写入密钥长度值
base->KS=0x10;

/* 写入最终的数据 */
base->DS=16;

// 一个 block 的写入
for(z=0;z<4;){
if(0U == (base->FIFOSTA & LTC_FIFOSTA_IFF_MASK)){
/* 复制数据到 FIFO */
base->IFIFO =*cycle_final_addr;
cycle_final_addr++;
z++;
}
}

// 等待结束
status=LTC_WAIT(base);
if(kStatus_Success!=status){
return status;
}

// 读出 CMAC
for(i=0;i<4;i++){
output[i]=base->CTX[i];
}

// 清除内部寄存器
base->CW=0xC000006D;
/* 复位 LTC , 清楚所有错误标志 */
base->COM = 0x03;
return status;
}

 

等待结束的函数过程如下:

/*
* 等待计算结束
* @param[in] base : LTC 寄存器指针
* @out :
* */

status_t LTC_WAIT(LTC_Type *base){
// 错误标志
bool error = false;
// 完成标志
bool done = false;
// 暂时存放 STA 值
uint32_t temp32=0;
/* 等待 'done' 或 'error' 标志. */
while ((!error) && (!done)){
temp32 = base->STA;
error = (0U != (temp32 & 0x100000U)) ? true : false;
done = (0U != (temp32 & 0x10000U)) ? true : false;
}
if (error){
/* 复位 LTC , 清楚所有错误标志 */
base->COM = 0x01;
return kStatus_Fail;

}
else{ /* 'done' */
// 清楚数据大小寄存器和模式寄存器
base->CW = (uint32_t)0x05;
// 清除 DONE 标志位
base->STA=0x00010000;
}
return kStatus_Success;
}

 

在第一次计算时,可以发现计算 4080Byte 的多个数据块的函数有两个,这是因为我加了一个汇编写的来进行对比。

C 语言实现的如下:

/*
* 计算 CMAC
* @param[in] base : LTC 寄存器指针
* @param[in] input : 输入数据的指针
* @param[in] inputEnd : 输入数据的末地址
* @param[in] cycle_count : 循环次数
* @out :
* */

static status_t LTC_CMAC_Calculate_Cycle(
LTC_Type *base,
const uint32_t *input,
uint32_t * inputEnd,
uint32_t cycle_count){
// 错误标志
bool error = false;
// 完成标志
bool done = false;
// 暂时存放 STA 值
uint32_t temp32=0;

status_t status = kStatus_Success;

uint32_t j=0;

//循环过程中一次 session 计算数据的大小(字节)
uint32_t cycle_once_sesseion_byte_count=0xFF0;

// session 内 block 循环次数
uint32_t cycle_once_block_cycle_count=1020;

// 最外层循环,是 session
for(uint32_t i=0;i<cycle_count;i++){
base->DS=cycle_once_sesseion_byte_count;
// 第二层循环,是每个 session 包含多少 字
for(j=0;j<cycle_once_block_cycle_count;){
// 等待 IFIFO 可以继续写入数据
if(0U == (base->FIFOSTA & LTC_FIFOSTA_IFF_MASK)){
/* 复制数据到 FIFO */
base->IFIFO =*input;
input++;
j++;
}
}
/* 等待 'done' 或 'error' 标志. */
while ((!error) && (!done)){
temp32 = base->STA;
error = (0U != (temp32 & 0x100000U)) ? true : false;
done = (0U != (temp32 & 0x10000U)) ? true : false;
}

if (error){
/* 复位 LTC , 清楚所有错误标志 */
base->COM = 0x01;
return kStatus_Fail;
}
else{ /* 'done' */
// 清楚数据大小寄存器和模式寄存器
base->CW = (uint32_t)0x05;
// 清除 DONE 标志位
base->STA=0x00010000;
}
// 错误标志
error = false;
// 完成标志
done = false;
// 设置模式(更新、非加密、AES、CMAC)
base->MD=0x00100600;
}
return status;
}

 

汇编版本如下:

/*
* 计算 CMAC
* @param[in] base : LTC 寄存器指针
* @param[in] input : 输入数据的指针
* @param[in] inputEnd : 输入数据的末地址
* @param[in] session_size : 算一次数据的大小
* @out :
* */

static status_t LTC_CMAC_Calculate_Cycle1(
LTC_Type *base,
const uint32_t *input,
uint32_t * inputEnd,
uint32_t session_size){
__asm("MOV R4, R3");
uint32_t MDData=0x100600;
__asm("MOV R3, R4");

// 将 LTC0 寄存器基地址(base)偏移 1984 的地址赋值给 r4.
__asm("MOV r4, #248");
__asm("MOV r5, #8");
__asm("MUL r4, r5,r4");
__asm("ADC r4, r4,r0");

/*************** 主循环:写入所有的数据 *********************************/
// 当前地址与末地址比较
__asm("MajorLoop: cmp r1,r2");
// 当前地址大于或等于末地址的时候,跳转到 正确结
__asm("BHS EndCalTrue");
// 往 DS 寄存器写入数据大小(r3)
__asm("STR r3, [r0,#16]");

/*************** 次循环:写入一次 session 大小的数据 *********************************/
// 当前地址加上 session 块大小为 r5
__asm("ADD r5, r1,r3");
// 当前地址与当前 session 地址比较
__asm("SecondLoop: CMP r1,r5");
// 当前地址大于或等于当前 session 地址的时候,跳转到 等待完成
__asm("BHS WaitDone");

// r6 存放 FIFOSTA 寄存器的值(半字)
__asm("FifoStatus: LDRH r6, [r4,#0]");
// 逻辑右移,右移 15位
__asm("LSR r6, r6,#15");
// 与 1 比较
__asm("CMP r6,#1");
// r7 大于或等于 1 时候,跳转到 继续获取FIFO状态
__asm("BHS FifoStatus");

// 当 r6 为 0,可继续写入 IFIFO。先将 输入的数据取出来放到 r6
__asm("LDR r6, [r1,#0]");
// 将 取出来的数值加载到 IFIFO
__asm("STR r6, [r4,#32]");
// 输入地址增加 4 (一个字)
__asm("ADD r1, r1,#4");
// 每增加一次,就比较一次当前地址与当前 session 末地址
__asm("B SecondLoop");

/*************** 等待完成 *********************************/
// r5 存放 STA 寄存器的值(字)
__asm("WaitDone: LDR r5, [r0,#72]");
// 逻辑右移,右移 20 位,看 EI 标志位
__asm("LSR r6, r5,#20");
// 与 1 比较
__asm("CMP r6,#1");
// EI 大于或等于 1 时候,表明出错,跳转到出错处理
__asm("BHS FalseHandle");

// 没有错误,逻辑右移,右移 16 位,看 DI 标志位
__asm("LSR r6, r5,#16");
// 与 1 比较
__asm("CMP r6,#1");
// DI 小于 1 时候,表明还没完成,继续等待
__asm("BLO WaitDone");

/*************** 完成处理方式 DI *********************************/
// 已经完成,则往 CW 写入 5,清楚数据大小寄存器和模式寄存器
__asm("MOV r5, #5");
__asm("STR r5, [r0,#64]");
// 往 STA 写入 0x1000,清除 DONE 标志位
__asm("MOV r5, #1");
__asm("LSL r6, r5,#16");
__asm("STR r6, [r0,#72]");
//设置模式(更新、非加密、AES、CMAC), 往 MD 写入 0x00100600
// __asm("MOV r5, #3");
// __asm("MOV r6, #1");
// __asm("LSL r6, r6,#11");
// __asm("ORR r6, r6,r5");
// __asm("LSL r6, r6,#9");
__asm("LDR r5, [r7,#20]");
__asm("STR r5, [r0,#0]");
// 跳转到主循环
__asm("B MajorLoop");

/*************** 错误出错处理方式 EI *********************************/
// 往 COM 加载 1
__asm("FalseHandle: MOV r5, #1");
__asm("STR r5, [r0,#48]");

// 返回 1
__asm("MOV r3, #1");
// 结束
__asm("B EndCalFalse");

/*************** 结束 *********************************/
__asm("EndCalTrue: MOV r3, #0");
__asm("EndCalFalse: NOP");
}

 

三、结果

    在 40Mhz 频率的情况下,我计算了 0x7000~0x7FFFF 的数据,总计都用时 126.8ms,差别不大。

四、参考文档

(1) NXP:《MKW36A512RM.pdf》

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★博文作者未开放评论功能