七、实验总结
经过本次实验,我掌握了常规加密算法中替换算法的原理,能够自己完成编写解密函数和相应地修改接收函数,更深刻理解了加密算法和解密算法的运作。此外,在发送明文时要注意在发送的数据末尾加上结束符'\\0',否则会出现乱码情况。
实验三 消息认证实验
一、实验目的
掌握消息摘要Hash算法的原理。 掌握消息认证的方法。 二、实验环境
硬件:ZXBee CC2530 节点板 2 块、USB 接口的 CC2530 仿真器,PC 机; 软件:Windows 7/Windows XP、IAR 集成开发环境、串口监控程序。 三、实验原理
假设所有发送及接收节点均工作在同一信号,每个节点均可监听到其他节点发送的数据。如何确信消息是来源声称的身份所发送的?同时,如何确保消息在传输过程中没有收
11
到篡改?解决方法是采用消息认证机制。 四、实验步骤
1)本实验程序可在《指导书》4.4节程序上进行修改,可节约时间。信道编号不用更改。 2)增加下列三个函数和有关定义。
#define MD_LEN 5 //消息摘要长度,字节(40bit) //简化版的消息摘要,长度为5字节 #define BLOCK_SIZE 16 //Hash处理的块长度,字节(128bit) //简化版的SHA处理数据块大小,长度为16字节 //字节循环左移函数 uint8 crol(uint8 c, uint8 b) { uint8 left=c<>(8-b); uint8 temp=left|right; return temp; } //简化版的SHA函数 void HashTransfer(uint8 *yText, uint8 *MD) { uint8 A=MD[0],B=MD[1],C=MD[2],D=MD[3],E=MD[4]; uint8 TA,TB,TC,TD; int i; for(i=0;i<20;i++) //只做20步,用一个逻辑函数 { TA=A;TB=B;TC=C;TD=D; A=crol(A,3)+(B&C)|(~B&D)+E+yText[i%MD_LEN]; B=TA; C=crol(TB,6); D=TC; E=TD; } MD[0]=MD[0]^A; MD[1]=MD[1]^B; MD[2]=MD[2]^C; MD[3]=MD[3]^D; MD[4]=MD[4]^E; } //消息摘要函数 void Hash(uint8 *Text, uint8 *MD, int TextLen)
12
{ } int i; int leftLen=TextLen; uint8 yText[BLOCK_SIZE]; uint8 IV[MD_LEN]={'\\x67','\\xEF','\\x98','\\x10','\\xC3'}; memcpy(MD, IV, MD_LEN); //初始化MD //简化版的分块,不足一个分块大小的后面补0 for(i=0;i<(TextLen/BLOCK_SIZE+1);i++) { if(leftLen/BLOCK_SIZE!=0) { memcpy(yText, Text+i*BLOCK_SIZE, BLOCK_SIZE); } else { memcpy(yText, Text+i*BLOCK_SIZE, leftLen); memset(yText+leftLen, 0, BLOCK_SIZE-leftLen); } } //简化版SHA转换 HashTransfer(yText, MD); leftLen=leftLen-BLOCK_SIZE; 3)增加一个发送数据函数rfSendData()。发送节点每隔 1s发送一次数据。发送数据的格式为:
字段名称 长度(字节) 消息内容长度 1 消息内容 2~256 HMAC 5 代码:
/*发送数据函数 */ void rfSendData(void) { uint8 PlainText[] = { \ //待发送的数据 uint8 MD[MD_LEN]; uint8 pTxData[128]; //用来存放发送数据 Hash(PlainText, MD, sizeof PlainText); //产生消息摘要 pTxData[0]=sizeof PlainText; memcpy(pTxData+1, PlainText, sizeof PlainText); memcpy(pTxData+(sizeof PlainText)+1, MD, MD_LEN);
13
} while(TRUE){ basicRfSendPacket(RECV_ADDR, pTxData, (sizeof PlainText)+6); //发送数据 halMcuWaitMs(1000); } 4)修改接收数据函数rfRecvData()。接收节点不断接收各个发送节点发送的信息,并通过串口显示。
/* 接收数据函数 */ void rfRecvData(void) { uint8 pRxData[128]; //用来存放接收到的数据 int rlen; basicRfReceiveOn(); //打开接收器 uint8 Len; unsigned char Flag; while (TRUE) { while(!basicRfPacketIsReady()); //等待直到数据准备好 rlen = basicRfReceive(pRxData, sizeof pRxData, NULL); //接收数据 if(rlen > 0) { //接收到数据 printf(pRxData); } } } 注意:这个时候pRxData是不是字符串?里面是否含有非ASCII字符?为什么?
5)编写一个消息摘要认证函数MDCertify( )。其中省略号部分自己填充完成。
/* 消息认证函数 */ unsigned char MDCertify (uint8 *Msg, uint8 *MD, uint8 Len) { uint8 MDx[MD_LEN]; unsigned char ret; //认证的结果,1:正确,0:不正确 Hash(Msg, MDx, Len); //产生消息摘要 …… //比较MD和MDx return ret; }
6)修改接收数据函数rfRecvData(),添加如下代码。接收节点不断接收各个发送节点发送的信息,然后认证MAC,并通过串口显示认证结果。
14
printf(pRxData); Len= *pRxData; //获得消息内容的长度 Flag=MDCertify (pRxData+1, pRxData+1+Len, Len); //认证消息 if(Flag) printf(\else printf(\ 注意:(1)上面代码应该放在函数rfRecvData()的什么地方,请自行思考。 (2) 如果希望串口输出不要出现乱码(非ASCII字符),上面的代码应该如何修改?
7)上面的消息认证过程中没有使用任何密钥,请自行修改代码,在消息的前面增加一个字节作为秘密值。这个秘密值各个小组可自行设定。发送节点的参考代码:
uint8 secret=15; //秘密值可修改,如自己的组号 uint8 Text[(sizeof PlainText)+1]; Text[0]=secret; for(i=0;i<(sizeof PlainText);i++) Text[i+1]=PlainText[i]; Hash(Text, MD, (sizeof PlainText)+1); //产生消息摘要 此时,接收节点先不用更改,观察消息认证是否正确。
8)接收节点收到消息之后,同样需要在消息的前面增加一个字节的秘密值,然后才进行消息认证。修改消息摘要认证函数MDCertify( )。增加一个形参secret。
unsigned char MDCertify (uint8 *Msg, uint8 *MD, uint8 Len, uint8 secret); 要求在消息认证函数里面,可以使用secret参数参与MD的计算。
五、实验结果
实验的结果注意观察下列情况,并对比(截图保存)。 1)发送节点发送消息,接收节点接收后显示,对应步骤4。
15