nrf51822-配对绑定实现过程
關于配對綁定的一些原理內(nèi)容這里不再重復介紹,看之前的幾篇文檔,靜態(tài)密碼,動態(tài)密碼,連接時觸發(fā)配對就可以了。
配對綁定的內(nèi)容可能比較難懂,升入的學習需要去看規(guī)范,將前面的幾篇相關文檔看一遍實驗一邊再去看規(guī)范能更好理解相關理論。
?
配對綁定是一個完整的過程,只是綁定是可選的,綁定簡單來說就是存儲一個長期秘鑰LTK,以方便以后加密。當然還分配了其他秘鑰,這里不涉及。
?
綁定是在配對之后,要明確 所謂配對 目的就是加密鏈路,以讓數(shù)據(jù)能加密傳輸,所以綁定肯定是在配對之后,因為綁定就是分發(fā)各種秘鑰,所以肯定要加密傳輸不然被別人竊聽到了,以后用 分發(fā)的秘鑰 再加密鏈路就不安全了。
?
總之 配對的目的 就是單純的加密鏈路,但是配對過程比較耗時(包括配對信息交換,用戶輸入配對碼或帶外傳輸配對碼,協(xié)議層的配對確認交換和隨機數(shù)交換以及確認驗證,都沒問題后才會生成鏈路加密秘鑰來加密鏈路),如果為了數(shù)據(jù)始終都是加密傳輸而每次連接都去配對的話就比較麻煩,所以又定義了一個綁定過程,綁定過程是在 配對后鏈路加密的情況下 分發(fā)一個 LTK(其他秘鑰這里不涉及),這個LTK就可以供以后直接加密鏈路,而不用進過繁瑣的配對過程。
?
PS:其實LTK分配之后,每次重新連接時的加密并不是用LTK直接加密鏈路,而是雙方交換一些信息(稱為會話秘鑰分散器),然后利用這些信息和LTK最終生成一個會話秘鑰,真正的加密是用這個會話秘鑰。
?
這里我實現(xiàn)一個 從機顯示配對碼,主機輸入配對碼的配對方式,配對碼為隨機的,從機的配對碼從串口打印出來。
主機輸入配對碼這個配對方式由配對信息交換時是否存在MITM標志以及從機是否有顯示裝置決定,所以我們配對信息中需要設置MITM標志,以及將I/O能力設置為有顯示。
?
綁定過程是否存在 取決于配對信息交換中是否設置了Bond標志,這里我們也要設置。
?
我們這里測試綁定時在綁定階段只分發(fā)LTK,其他秘鑰這里不涉及
?
配對綁定大致分為3個階段:
?
1:配對信息的交換
2:生成STK(短期秘鑰)加密鏈路
3:鏈路加密后就可以安全分發(fā)各種秘鑰了。
?
?
大致的過程圖如下所示
?
| ? |
?
?
大部分的工作協(xié)議棧都做好了,上層要處理的就是設置一些 參數(shù)以及處理幾個事件。
?
首先要處理?BLE_GAP_EVT_SEC_PARAMS_REQUEST?事件,當手機發(fā)來配對請求時就會收到這個事件。 需要調(diào)用 回復api回復配對參數(shù)。?
簡要代碼如下。
?????? case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("receive pair req\r\n");
???????????????????? init_sec();
??????????? err_code =?sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &g_pair_params, &keyset);
???????????????????? printf("err_code :%d\r\n",err_code);
??????????? break;
?
sd_ble_gap_sec_params_repl 的第三個參數(shù)g_pair_params參數(shù)就是要回復的配對綁定參數(shù)設置
具體設置如下
ble_gap_sec_params_t? g_pair_params;
void init_sec(void){
?????? g_pair_params.bond = 1;
?????? g_pair_params.io_caps = 0;
?????? g_pair_params.oob = 0;
?????? g_pair_params.mitm = 1;
?????? g_pair_params.min_key_size = 7;
?????? g_pair_params.max_key_size = 16;
??????
?????? g_pair_params.kdist_central.enc = 1;
?????? g_pair_params.kdist_central.id = 0;
?????? g_pair_params.kdist_central.sign = 0;
?????? g_pair_params.kdist_periph.enc = 1;
?????? g_pair_params.kdist_periph.id = 0;
?????? g_pair_params.kdist_periph.sign = 0;
}
IO能力及設備的輸入輸出能力,有以下幾個值
#define ?????? BLE_GAP_IO_CAPS_DISPLAY_ONLY?? 0x00
#define ?????? BLE_GAP_IO_CAPS_DISPLAY_YESNO?? 0x01
#define ?????? BLE_GAP_IO_CAPS_KEYBOARD_ONLY?? 0x02
#define ?????? BLE_GAP_IO_CAPS_NONE?? 0x03
#define ?????? BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY?? 0x04
設置了MITM為1和io能力設置為0,這個組合表示配對過程中 設備會顯示配對碼,主機需要輸入對應配對碼。
Bond 設置為1表示需要綁定,則配對會存在綁定過程即LTK等秘鑰的分發(fā)。
?
具體分發(fā)哪些秘鑰也是可以控制,這里只說LTK,所以上面的設置中只設置了相互分發(fā)長期秘鑰LTK,其他不需要設置
?
PS:之所以相互分發(fā)LTK是因為,如果手機本次作為主機連接了設備,設備作為從機,配對綁定后斷開連接,當再次連接時如果手機依然是作為主機去連設備,那么加密時就需要? 從機分發(fā)給手機的LTK。但是有的應用可能主從角色并不是固定的,下次可能 是設備作為主機去連手機那么 加密時 就需要上次綁定時 手機發(fā)給設備的LTK。 上面的情況我們設置了相互分發(fā)LTK,其實一般都是手機一直作為主機去連設備,這種情況我們只需要g_pair_params.kdist_periph.enc = 1;就可以了
?
既然是配對會存在分發(fā)秘鑰過程,那么協(xié)議棧交換的秘鑰存儲在哪里以在綁定完成后返回給上層呢。
返回的秘鑰就是存在?sd_ble_gap_sec_params_repl?的第四個參數(shù)keyset中
?
設置如下,即我們需要自己創(chuàng)建變量來存儲分發(fā)的秘鑰,因為這只用了LTK,所以其他兩個指針設置為NULL就行了。
ble_gap_enc_key_t my_enc_key;
ble_gap_enc_key_t my_enc_key_center;
?
ble_gap_sec_keyset_t keyset;
void init_keyset(void){
?????? keyset.keys_periph.p_enc_key = &my_enc_key;
?????? keyset.keys_periph.p_id_key = NULL;
?????? keyset.keys_periph.p_sign_key = NULL;
??????
?????? keyset.keys_central.p_enc_key = &my_enc_key_center;
?????? keyset.keys_central.p_id_key = NULL;
?????? keyset.keys_central.p_sign_key = NULL;??????
??????
}
?
當?sd_ble_gap_sec_params_repl按如上設置回復后,設備這邊就會顯示配對碼需要讓手機輸入,因為沒有顯示 屏這里通過串口打印出來。
?
上層會收到協(xié)議棧的BLE_GAP_EVT_PASSKEY_DISPLAY提交上來的顯示事件。直接將配對碼打印出來
case?BLE_GAP_EVT_PASSKEY_DISPLAY:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("passkey: ");
???????????????????? for ( int i = 0; i < 6; i++ ){
??????????????????????????? ???????????????????? printf("%c",p_ble_evt->evt.gap_evt.params.passkey_display.passkey[i]);
???????????????????? }
???????????????????? printf("\r\n");
???????????????????? break;
?
這之后基本都是協(xié)議棧內(nèi)部進行了,當綁定完成后上層會收到協(xié)議棧的BLE_GAP_EVT_AUTH_STATUS事件表示完成了秘鑰的分發(fā)。
?
我們在收到這個事件后打印相關的秘鑰信息
????????????? case?BLE_GAP_EVT_AUTH_STATUS:
???????????????????? flag = 1;
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("keyset dispatch done\r\n");
???????????????????? printf("LTK :");
???????????????????? for(int i = 0; i < my_enc_key.enc_info.ltk_len; i++){
??????????????????????????? printf("%x",my_enc_key.enc_info.ltk[i]);
???????????????????? }
????????????????????
???????????????????? printf("?? AUTH: %d ",my_enc_key.enc_info.auth);
???????????????????? printf("?? LTK length: %d \r\n",my_enc_key.enc_info.ltk_len);
???????????????????? printf("EDIV: %x ",my_enc_key.master_id.ediv);
???????????????????? printf("rand:");
???????????????????? for(int i = 0; i < 8; i++){
??????????????????????????? printf("%x",my_enc_key.master_id.rand[i]);
???????????????????? }
???????????????????? printf("\r\n");
???????????????????? break;
ps: EDIV 和RAND是和LTK一起發(fā)送的,你可以將其看做是LTK的標示,當手機以后請求用LTK來進行加密時就會發(fā)送給從機EDIV和RAND讓其確定要使用的LTK
?
?
到這里配對綁定過程就結束了,前面說過綁定的目的是為了下次鏈接需要安全鏈路時不再進行繁瑣的配對過程,所以一般手機和一個設備綁定過后,當下次再連接時都會直接用以前綁定時的LTK來發(fā)起加密請求。
?
我們在收到這個信息后打印了加密請求的一些信息,打印了我們回復的LTK和請求的EDIV,RAND看是不是和上面綁定過程時分配的一樣。
????????????? case BLE_GAP_EVT_SEC_INFO_REQUEST:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("enc need: %d? id need:%d?? sign need:%d\r\n",
????????????????????????????????????????? p_ble_evt->evt.gap_evt.params.sec_info_request.enc_info,
????????????????????????????????????????? p_ble_evt->evt.gap_evt.params.sec_info_request.id_info,?
????????????????????????????????????????????p_ble_evt->evt.gap_evt.params.sec_info_request.sign_info);?????????????
???????????????????? printf("RSP:? LTK :");
???????????????????? for(int i = 0; i < my_enc_key.enc_info.ltk_len; i++){
??????????????????????????? printf("%x",my_enc_key.enc_info.ltk[i]);
???????????????????? }
???????????????????? printf("\r\nEDIV :%x? RANDOM:",
??????????????????????????? p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv);
????????????????????
???????????????????? for(int i = 0; i< 8; i++){
?????? ???????????????????? printf("%x",
???????????????????? p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand[i]);
???????????????????? }
????????????????????
???????????????????? err_code=sd_ble_gap_sec_info_reply(m_conn_handle, ?&(my_enc_key.enc_info), NULL, NULL);
???????????????????? printf("\r\nencryption rsp err_code:%d\r\n",err_code);
???????????????????? break;
?
關于如何讓手機發(fā)起配對,我們還是設置CCCD的訪問安全要求 來實現(xiàn)在點擊notify時,設備會返回安全不足,然后手機便會發(fā)起配對。具體可以看之前的
靜態(tài)/動態(tài)配對碼 的文章。
程序運行后如下圖所示,連接上后,點擊notify便會觸發(fā)配對。
首先收到配對請求
之后協(xié)議棧會上傳配對碼,通過串口顯示,手機端輸入該配對碼。
最后配對完成,打印了LTK和EDIV,RAND.
?
然后斷開連接,再次連接設備,可以看到手機這次直接就發(fā)加密請求過來了。請求中的EDIV和RAND也和之前綁定時分配的一樣。
?
?
最后貼一下相關代碼,代碼都是關于幾個事件的處理,我全部都添加在了on_ble_evt這個事件處理函數(shù)中。
?
ble_gap_sec_params_t? g_pair_params;
?
void init_sec(void){
?????? g_pair_params.bond = 1;
?????? g_pair_params.io_caps = 0;
?????? g_pair_params.oob = 0;
?????? g_pair_params.mitm = 1;
?????? g_pair_params.min_key_size = 7;
?????? g_pair_params.max_key_size = 16;
??????
?????? g_pair_params.kdist_central.enc = 1;
?????? g_pair_params.kdist_central.id = 0;
?????? g_pair_params.kdist_central.sign = 0;
?????? g_pair_params.kdist_periph.enc = 1;
?????? g_pair_params.kdist_periph.id = 0;
?????? g_pair_params.kdist_periph.sign = 0;
??????
}
ble_gap_sec_keyset_t? sec_keyset;
?
ble_gap_conn_sec_mode_t sec_mode;
?
ble_gap_enc_key_t my_enc_key;
?
ble_gap_enc_key_t my_enc_key_center;
?
ble_gap_sec_keyset_t keyset;
void init_keyset(void){
?????? keyset.keys_periph.p_enc_key = &my_enc_key;
?????? keyset.keys_periph.p_id_key = NULL;
?????? keyset.keys_periph.p_sign_key = NULL;
??????
?????? keyset.keys_central.p_enc_key = &my_enc_key_center;
?????? keyset.keys_central.p_id_key = NULL;
?????? keyset.keys_central.p_sign_key = NULL;??????
??????
}
?
?
uint8_t step_count = 0;
static void on_ble_evt(ble_evt_t * p_ble_evt)
{
??? uint32_t???????????????????????? err_code;
???
??? switch (p_ble_evt->header.evt_id)
??? {
??????? case BLE_GAP_EVT_CONNECTED:
??????????? err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
??????????? APP_ERROR_CHECK(err_code);
??????????? m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
?????????????
?????????????????????init_keyset();
???????????????????? printf("\r\nconnected\r\n");
??????????? break;
???????????
??????? case BLE_GAP_EVT_DISCONNECTED:
??????????? err_code = bsp_indication_set(BSP_INDICATE_IDLE);
??????????? APP_ERROR_CHECK(err_code);
??????????? m_conn_handle = BLE_CONN_HANDLE_INVALID;
???????????????????? printf("\r\ndisconnected");
??????????? break;
?
??????? case?BLE_GAP_EVT_SEC_PARAMS_REQUEST:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("receive pair req\r\n");
?????????????????????init_sec();
??????????? err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &g_pair_params, &keyset);
???????????????????? printf("err_code :%d\r\n",err_code);
????????????? // APP_ERROR_CHECK(err_code);
??????????? break;
????????????? case BLE_GAP_EVT_PASSKEY_DISPLAY:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("passkey: ");
???????????????????? for ( int i = 0; i < 6; i++ ){
??????????????????????????? printf("%c",p_ble_evt->evt.gap_evt.params.passkey_display.passkey[i]);
???????????????????? }
???????????????????? printf("\r\n");
???????????????????? break;
??????? case BLE_GATTS_EVT_SYS_ATTR_MISSING:
??????????? // No system attributes have been stored.
??????????? err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
??????????? APP_ERROR_CHECK(err_code);
??????????? break;
????????????? case?BLE_GAP_EVT_AUTH_STATUS:
???????????????????? printf("\r\n\r\n step : %d\r\n",++step_count);
???????????????????? printf("keyset dispatch done\r\n");
???????????????????? printf("LTK :");
???????????????????? for(int i = 0; i < my_enc_key.enc_info.ltk_len; i++){
??????????????????????????? printf("%x",my_enc_key.enc_info.ltk[i]);
???????????????????? }
????????????????????
???????????????????? printf("?? AUTH: %d ",my_enc_key.enc_info.auth);
???????????????????? printf("?? LTK length: %d \r\n",my_enc_key.enc_info.ltk_len);
???????????????????? printf("EDIV: %x ",my_enc_key.master_id.ediv);
???????????????????? printf("rand:");
???????????????????? for(int i = 0; i < 8; i++){
??????????????????????????? printf("%x",my_enc_key.master_id.rand[i]);
???????????????????? }
???????????????????? printf("\r\n");
???????????????????? break;
????????????????????
????????????? case?BLE_GAP_EVT_SEC_INFO_REQUEST:
???????????????????? printf("\r\nenc need: %d? id need:%d? ?sign need:%d\r\n",p_ble_evt->evt.gap_evt.params.sec_info_request.enc_info,
???????????????????????????????????????????????????????????????????????????????????????????????????????? ?????? p_ble_evt->evt.gap_evt.params.sec_info_request.id_info,
???????????????????????????????????????????????????????????????????????????????????????????????????????? ?????? p_ble_evt->evt.gap_evt.params.sec_info_request.sign_info);????????????????????
????????????????????
???????????????????????????
???????????????????? printf("RSP:? LTK :");
???????????????????? for(int i = 0; i < my_enc_key.enc_info.ltk_len; i++){
??????????????????????????? printf("%x",my_enc_key.enc_info.ltk[i]);
???????????????????? }
???????????????????? printf("\r\nEDIV :%x? RANDOM:",p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.ediv);
???????????????????? for(int i = 0; i< 8; i++){
??????????????????????????? printf("%x",p_ble_evt->evt.gap_evt.params.sec_info_request.master_id.rand[i]);
???????????????????? }
????????????????????
???????????????????? err_code = sd_ble_gap_sec_info_reply(m_conn_handle, &(my_enc_key.enc_info), NULL, NULL);
?????????????
???????????????????? printf("\r\nencryption rsp err_code:%d\r\n",err_code);
???????????????????? break;
??????? default:
??????????? // No implementation needed.
??????????? break;
??? }
}
總結
以上是生活随笔為你收集整理的nrf51822-配对绑定实现过程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: BLE-NRF51822教程10—动态密
- 下一篇: BLE 包结构及传输速率