BLE 加密详解
Posted 车子 chezi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BLE 加密详解相关的知识,希望对你有一定的参考价值。
BLE 协议里面的加密,可以用硬件实现,也可以用软件实现。本文讨论如何用软件实现,借用开源代码。
求 MIC
MIC 占 4 个字节。先说如何计算 MIC。
B0
B0 = 0x49 || nonce || length
B0 的格式是协议规定的
注意:||
表示串联,左边是低字节,右边是高字节
length 占 2 个字节
Nonce
低 39 bits 是 packetCounter,然后是 1 bit 的方向位(M 到 S 是 1,S 到 M 是 0),最后是 8 字节的 IV
B1
B1 = 0x0001 || headermasked || 0x00000000000000000000000000
headermasked 是什么,其实就是 2 bits 的 LLID
B2 和 B3
B2 contains the payload data from octets 0 to 15.
B3 contains the payload data from octets 16 to 26.
计算公式
弄明白了 B0-B3,就能看懂协议上的数据了。
补充一下,B0 中的 packetCounter 是 0,方向是 1;B1 中的 03 表示 LLID;B2 中的 06 是 payload,是 START_ENC_RSP 的明文。
计算 MIC 的公式是:
因为没有 B3,所以只用计算前 3 行。公式中的 X0 是协议里面的 X1,公式中的 X1 是协议里面的 X2,公式中的 X2 是协议里面的 X3,只是叫法不一样。
先把结果摆出来,看看怎么编程实现。
代码
char *SK_str = "99AD1B5226A37E3E058E3B8E27C2C666"; // MS-LS
char *B0_str = "49000000008024ABDCBABEBAAFDE0001"; // LS-MS
char *B1_str = "00010300000000000000000000000000"; // LS-MS
char *B2_str = "06000000000000000000000000000000"; // LS-MS
int hex_string_to_u8(const char *hex_str, char *out)
{
if(strlen(hex_str) & 1){
printf("字符串的长度是奇数!");
return -1;
}
char byte[3] = {0};
const char *p = hex_str;
int j = 0;
for(int i=0; i<strlen(hex_str); i+=2){
memcpy(byte, &p[i], 2);
out[j++] = strtol(byte, NULL, 16);
}
return 0;
}
int main(void)
{
uint8_t temp[16] = {0};
uint8_t X1[16] = {0};
uint8_t X2[16] = {0};
uint8_t X3[16] = {0};
hex_string_to_u8(SK_str, SK);
hex_string_to_u8(B0_str, B0);
hex_string_to_u8(B1_str, B1);
hex_string_to_u8(B2_str, B2);
// X1 = E(B0)
// X2 = E(X1 xor B1)
// X3 = E(X1 xor B2)
aes128_calc_cyphertext(SK, B0, X1);
hexdump("X1", X1, 16);
xor(X1, B1, temp, 16);
aes128_calc_cyphertext(SK, temp, X2);
hexdump("X2", X2, 16);
xor(X2, B2, temp, 16);
aes128_calc_cyphertext(SK, temp, X3);
hexdump("X3", X3, 16);
hexdump("MIC", X3, 4);
}
hex_string_to_u8 这个函数的作用是把十六进制的字符串转成数字,解释见我的博文:C语言16进制字符串转数字_车子(chezi)-CSDN博客
SK_str 经这样转换后,其实是大端序,低地址存放着高字节;
B0_str 等转换后是小端序;
aes128_calc_cyphertext 这个函数是别人的代码,注释是我加的。
/**
* @brief Calculate ciphertext with AES-128
* @key: [in] note: big endian
* @plaintext :[in] little endian
* @cyphertext:[out] little endian
* @returns void
*/
void aes128_calc_cyphertext(uint8_t key[16],
uint8_t plaintext[16], uint8_t cyphertext[16])
{
uint32_t rk[RKLENGTH(KEYBITS)];
int nrounds = rijndaelSetupEncrypt(rk, &key[0], KEYBITS);
rijndaelEncrypt(rk, nrounds, plaintext, cyphertext);
}
xor 这个函数是我写的,用来求异或。文末会附上完整的代码。
hexdump 是一个简单的打印函数。
运行结果是:
X1: 71 2E AA AA E6 06 03 52 1D 24 5E 50 78 6E EF E4
X2: DE BC 43 78 2A 02 26 75 FC A0 AA 6F 08 54 F1 AB
X3: 63 99 91 3F ED E5 FA 11 1B DB 99 3B BF B9 BE 06
MIC: 63 99 91 3F
和协议相符。
求加密数据
Ai
对照协议中的
A0 = 01000000008024ABDCBABEBAAFDE0000
A1 = 01000000008024ABDCBABEBAAFDE0001
注意:
- counter i 是块计数器,在加密净荷的前 16 字节时,块计数器应设为 0x0001
- 在加密净荷的后 11 字节时,块计数器应设为 0x0002
- 对 MIC 进行加密的时候,块计数器应设为 0x0000
因为要加密的净荷只有 1 个字节,所以没有 A2
计算公式
协议给出:
S0 = ae3e6577f64a8f25408c9c10d53acf8e
S1 = 99190d88f4aa1b60b97ecfe6f5fee777
S0 = E(A0)
S1 = E(A1)
公式中的 CMIC 应该是 S0 的低 4 个字节
公式中的 CBlock1 就是 S1
第二个公式中的 Block1 表示净荷的前 16 字节,就是前文的 B2;Block2 表示净荷的后 11 字节,就是前文的 B3
综上,计算出 S0 和 S1 是关键
代码
// S0 = E(A0)
// S1 = E(A1)
uint8_t S0[16] = {0};
uint8_t S1[16] = {0};
hex_string_to_u8(A0_str, A0);
hex_string_to_u8(A1_str, A1);
aes128_calc_cyphertext(SK, A0, S0);
hexdump("S0", S0, 16);
aes128_calc_cyphertext(SK, A1, S1);
hexdump("S1", S1, 16);
// encrypted MIC = S0 xor X3
xor(S0, X3, temp, 4);
hexdump("encrypted MIC", temp, 4);
// encrypted packet payload = S1 xor B2
xor(S1, B2, temp, 4);
hexdump("encrypted packet payload", temp, 1);
执行结果是:
S0: AE 3E 65 77 F6 4A 8F 25 40 8C 9C 10 D5 3A CF 8E
S1: 99 19 0D 88 F4 AA 1B 60 B9 7E CF E6 F5 FE E7 77
encrypted MIC: CD A7 F4 48
encrypted packet payload: 9F
和协议的结果比较一下:
So, encrypted packet payload = 9F
encrypted MIC = CDA7F448
遗留问题
对于 5.0,payload 的长度可以是 255,那是不是还有 B4,B5,B6,…?
参考资料
【1】《Bluetooth Core Specification v 5.0》 P2699
【2】http://www.efgh.com/software/rijndael.htm
【3】《Bluetooth Low Energy :The Developer’s Handbook》7.9. Encryption
完整代码
rijndael.h
// =============================== RIJNDAEL.H ===============================
// from http://www.efgh.com/software/rijndael.htm,
// License: Public Domain,
// Author: Philip J. Erdelsky
#ifndef H__RIJNDAEL
#define H__RIJNDAEL
#include <stdint.h>
int rijndaelSetupEncrypt(uint32_t *rk, const uint8_t *key, int keybits);
int rijndaelSetupDecrypt(uint32_t *rk, const uint8_t *key, int keybits);
void rijndaelEncrypt(const uint32_t *rk, int nrounds, const uint8_t plaintext[16], uint8_t ciphertext[16]);
void rijndaelDecrypt(const uint32_t *rk, int nrounds, const uint8_t ciphertext[16], uint8_t plaintext[16]);
#define KEYBITS 128
#define KEYLENGTH(keybits) ((keybits)/8)
#define RKLENGTH(keybits) ((keybits)/8+28)
#define NROUNDS(keybits) ((keybits)/32+6)
#endif
rijndael.c
//=============================== RIJNDAEL.C ===============================
// from http://www.efgh.com/software/rijndael.htm,
// License: Public Domain,
// Author: Philip J. Erdelsky
#define FULL_UNROLL
//#include <stdio.h>
#include "rijndael.h"
typedef uint32_t u32;
typedef uint8_t u8;
static const u32 Te0[256] =
{
0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
};
static const u32 Te1[256] =
{
0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
0xad3f9292U如何使用 Bluez5.50 使用 BLE 连接加密数据