为了支持加密,相比未加密数据帧,对帧格式进行两处更改。
1、当帧被加密时,设置加密有效位。即网络层帧头第一个字F_ENCRYPT_OS_MSK置1.。
2、在网络层帧头往后加3个字节。如下所示,分别为CTR、ICHK和MAC,CTR用于存储加密计数器初始值,ICHK用于存储FCS(帧校验序列),这里默认使用求和校验。MAC用于存储消息认证码。这将会使允许的有效最大应用程序有效负载减少3。
1、初始化向量( IV )
初始化向量是一个32位的无符号对象。 它在文件nwk_security.c中声明和定义。 用户可以根据需要进行更改。默认为0x87654321.
2、密钥(KEY)
密钥是一个128位值,它初始化为16字节的字符数组。加密算法适用于32位无符号长对象。初始化密钥是字符数组,根据需要进行字节交换以适应不同微控制器的大小端。 必须这样做才能保证具有不同本机端表示的微控制器能有相同的加密结果,密钥初始化值为SimpliciTI's Key。
3、消息认证码(MAC)
消息认证码是一个8位的常量。当有效负载被解密时,将MAC值与期望值进行比较。它不参与消息完整性检查,默认值为0xA5。
4、计数器(COUNTER)
计数器是32位无符号长整形对象。对于基于连接的应用程序,每个连接的计数器的初始值都是在运行时生成的。 这些连接基本上代表用户应用程序,建立连接时,在链接会话中交换初始值。对于网络应用程序,计数器值的导出方式不同,因为网络应用程序不是基于连接的。
1、加密过程
首先由初始化向量和计数器值和密钥进行串联,成为64位数据,通过XTEA产生8个字节的加密数据,然后将准备加密的八个字节分别于加密数据进行异或操作,结果即为加密后的数据。为保证密码块不会重复,每一次加密改变计数器值,而计数器在较长时间内不会重复,负载长度有限,则不会出现重复代码块。更改每个加密的计数器可确保每个加密操作都有一个唯一的块。由于加密中使用的密钥长度为128位,在得到的64位密码块和要发送的(下一个)64位纯文本之间进行独占的按位逻辑异或。如果剩余的纯文本小于64位,则丢弃额外的密码块。 如果纯文本仍然存在,则创建下一个密码块,增加计数值。
2、解密过程
解密是通过加密与加密端相同的块来完成的。 然后,所得到的密码块与接收的密码流进行异或,以再现原始的纯文本。因为与相同的数进行两次异或即为本身。 由于密码流与原始纯文本的长度匹配,因此使用相同的过程来纠正非64位倍数的流。显然,这种方案取决于保持计数器同步。
在密码学中,微型加密算法(Tiny Encryption Algorithm,TEA)是一种易于描和执行的块密码,通常只需要很少的代码就可实现。其设计者是剑桥大学计算机实验室的大卫·惠勒与罗杰·尼达姆。这项技术最初于1994年提交给鲁汶的快速软件加密的研讨会上,并在该研讨会上演讲中首次发表。XTEA是TEA的升级版,增加了更多的密钥表,移位和异或操作等等,设计者是Roger Needham, David Wheeler
加密过程:
代码实现:
/******************************************************************************
* @fn xtea_encipher
*
* @brief XTEA encipher algorithm. Calling arguments removed from public
* domain code and static-scope values used instead.
*
* input parameters
*
* output parameters
*
* @return void
*/
void xtea_encipher(void)
{
uint32_t v0=sMsg[0], v1=sMsg[1];
uint16_t i;
uint32_t sum=0, delta=0x9E3779B9;
for(i=0; i
v0 += (((v1 <<4) ^ (v1 >> 5)) + v1) ^ (sum + sKey.keyL[sum & 3]);
sum += delta;
v1 += (((v0 <<4) ^ (v0 >> 5)) + v0) ^ (sum + sKey.keyL[(sum>>11) & 3]);
}
sMsg[0]=v0;
sMsg[1]=v1;
}
SIMPLICI TI在信息加密部分主要提供了两个主要函数,即加密数据帧,以及解密数据帧,nwk_setSecureFrame和nwk_getSecureFrame。nwk_setSecureFrame首先输入的判断计数器初始值是否为空,为空则产生一个随机数赋值给本地计数器,然后给CTR、ICHK 和MAC赋值,然后进行加密, F_ENCRYPT_OS置位,最后保存locnt到ctr。nwk_getSecureFrame则是nwk_setSecureFrame的逆过程,首先进行计数器的校验,如果匹配,进行解密,然后进行MAC和FCS的校验。
nwk_setSecureFrame:
/******************************************************************************
* @fn nwk_setSecureFrame
*
* @brief Called from NWK to secure a frame.
*
* input parameters
* @param frame - pointer to frame to secure
* @param msglen - length of message
* @param ctr - pointer to the counter used in the cipher block. This will
* be NULL if a network application is sending a frame. Since
* these are not connection-based there is no counter sync
* issue but we still need a counter value. A random value
* is used.
*
* output parameters
* @param cntStart - counter is updated during encryption.
*
* @return void
*/
void nwk_setSecureFrame(mrfiPacket_t *frame, uint8_t msglen, uint32_t *ctr)
{
uint32_t locCnt;
/* If an encrypted frame is to be sent to a non-connection based port use a
* random number as the lsb counter value. In this case only the lsb is used
* for a counter value during decryption. Not as secure but there are still
* the 32 bits in the IV.
*/
locCnt = ctr ? *ctr : MRFI_RandomByte();
/* place counter value into frame */
PUT_INTO_FRAME(MRFI_P_PAYLOAD(frame), F_SEC_CTR_OS, (uint8_t)(locCnt & 0xFF));
/* Put MAC value in */
nwk_putNumObjectIntoMsg((void *)&sMAC, (void *)(MRFI_P_PAYLOAD(frame)+F_SEC_MAC_OS), sizeof(secMAC_t));
/* Put FCS value in */
{
secFCS_t fcs = calcFCS(MRFI_P_PAYLOAD(frame)+F_SEC_MAC_OS, msglen+sizeof(secMAC_t));
nwk_putNumObjectIntoMsg((void *)&fcs, (void *)(MRFI_P_PAYLOAD(frame)+F_SEC_ICHK_OS), sizeof(secFCS_t));
}
/* Encrypt frame */
msg_encipher(MRFI_P_PAYLOAD(frame)+F_SEC_ICHK_OS, msglen+sizeof(secMAC_t)+sizeof(secFCS_t), &locCnt);
/* Set the Encryption bit */
PUT_INTO_FRAME(MRFI_P_PAYLOAD(frame), F_ENCRYPT_OS, F_ENCRYPT_OS_MSK);
/* Update the counter if it was a "real" counter. */
if (ctr)
{
*ctr = locCnt;
}
return;
}
nwk_getSecureFrame:
/******************************************************************************
* @fn nwk_getSecureFrame
*
* @brief Called from NWK to get a secure a frame and decrypt.
*
* input parameters
* @param frame - pointer to frame containing encrypted message
* @param msglen - length of message
* @param ctr - pointer to the counter used in the cipher block. This will
* be NULL if a network applicaiton is getting a frame. Since
* these are not connection-nbased there is no counter sync
* issue but we still need a counter value.
*
* output parameters
* @param cntStart - counter is updated during decryption. If decryption fails
* this value is not changed.
*
* @return Returns non-zero if frame decryption is valid, otherwise returns 0.
*/
uint8_t nwk_getSecureFrame(mrfiPacket_t *frame, uint8_t msglen, uint32_t *ctr)
{
uint8_t rc = 1;
uint8_t dOne= 0;
uint8_t cntHint = GET_FROM_FRAME(MRFI_P_PAYLOAD(frame), F_SEC_CTR_OS);
uint32_t locCnt, frameCnt;
/* Construct proposed CTR values */
/* Just like encryption, we may be talking to a non-connection based
* peer in which case the counter value is represented by the lsb byte
* conveyed in the frame.
*/
locCnt = ctr ? *ctr : cntHint;
frameCnt = (locCnt & 0xFFFFFF00) + cntHint;
do
{
/* See if counters match */
if (locCnt == frameCnt)
{
/* When the counters appear to match is the only time we actually decipher
* the message. It is the only time we can do so since out-of-sync lsb counter
* values guarantees that something is wrong somewhere. Decryption is successful
* only if the MAC and FCS values match. The message is left as-is after the
* decipher attempt. Either it appears valid or is doesn't and is discarded.
* There is no recovery attempt if the counters match but the MAC or FCS do
* not. It is considered a rogue message.
*/
msg_decipher(MRFI_P_PAYLOAD(frame)+F_SEC_ICHK_OS, msglen-1, &locCnt);
/* Get MAC and make sure it matches. A failure can occur if a replayed frame happens
* to have the correct counter sync value but was encoded with the wrong complete
* counter value. Otherwise the MAC values must match when the counter values are equal.
*/
{
secMAC_t mac;
nwk_getNumObjectFromMsg((void *)(MRFI_P_PAYLOAD(frame)+F_SEC_MAC_OS), (void *)&mac, sizeof(secMAC_t));
if (mac != sMAC)
{
rc = 0;
}
}
/* FCS check... */
{
secFCS_t fcs;
nwk_getNumObjectFromMsg((void *)(MRFI_P_PAYLOAD(frame)+F_SEC_ICHK_OS), (void *)&fcs, sizeof(secFCS_t));
if (fcs != calcFCS(MRFI_P_PAYLOAD(frame)+F_SEC_MAC_OS, msglen-1-sizeof(secMAC_t)))
{
rc = 0;
}
}
/* we're done. */
dOne= 1;
}
else
{
/* Uh oh. Counters don't match. Try and resync. We need to distinguish among
* missed frames, duplicates and rogues plus account for counter wrap.
*/
if (frameCnt > locCnt)
{
/* frameCnt is bigger. Second part of test below takes care of
* the unlikely case of a complete counter wrap (msb's all 0) in
* which case the test will incorrectly fail when the count is
* actually within the (wrapped) window. #ifdef'ed to avoid compiler
* warning in case user sets CNT_WINDOW to 0 (pointless comparison of
* unsigned value).
*/
if (((frameCnt-CTR_WINDOW) <= locCnt)
#if CTR_WINDOW > 0
|| (frameCnt
)
{
/* Value within window. We probably missed something. Adjust and decipher.
* If locCnt is less because it wrapped and frameCnt didn't it means that
* it's a duplicate or late frame. In that case the following will lead to
* a decryption that fails sanity checks which is OK because the frame will
* be correctly rejected.
*/
locCnt = frameCnt;
}
else
{
/* It's either a rogue or a really old duplicate packet. In either case
* we dismiss the frame.
*/
rc = 0;
dOne= 1;
}
}
else
{
/* locCnt is bigger. The only way the frame can be valid is if the
* counter wrapped causing frameCnt to appear to be smaller. Wrap the
* counter and decrypt. If the frame isn't valid, i.e., it's late,
* a duplicate, or a rogue, the decryption will fail sanity checks and
* the frame will be correctly rejected. The following arithmetic works
* correctly without a special test for the complete counter wrap case.
*/
frameCnt += 0x100; /* wrap the hint-based counter */
if (((frameCnt-CTR_WINDOW) <= locCnt))
{
/* An lsb wrap but still within window. We probably missed something.
* Adjust (with wrap) and decrypt.
*/
locCnt = frameCnt;
}
else
{
/* rogue frame */
rc = 0;
dOne= 1;
}
}
}
} while (!done);
if (ctr && rc)
{
/* Only update the counter if the count was a "real" one and the
* decryption succeeded.
*/
*ctr = locCnt;
}
return rc;
}
#endif /* SMPL_SECURE */