nordicBLE交流群498676838
這一講介紹配對的一些相關理論知識,並且介紹如何實現”靜態密碼”的設定
程序是基於sdk9.0 下的 uart demo
另外 測試使用的手機app是 IOS下的lightblue。
這裡准確的說法應該是叫配對碼,而不是密碼。輸入這個這個配對碼是配對過程中可選的一部分
介紹如何設置靜態密碼前先介紹一下配對的相關知識(後面都直接叫密碼,而不是配對碼)
起初未提供安全性的兩個設備如果希望做一些需要安全性的工作,就必須先配對。配對涉及兩個設備的身份認證,鏈路加密。如果配對時設置了綁定位,隨後還會有一個秘鑰分配。分配的秘鑰用戶可以存儲在flash中這樣兩個設備再第二次重連時的安全啟動會更快。而不需要像第一次一樣需要再啟動整個配對過程。
配對的第一個過程首先是配對信息的交換,這些信息用於確定認證方式,以及後續是否需要分配密鑰以及分配哪些密鑰。
交換的信息包括:
兩端設備的輸入輸出能力如:是否有顯示屏,鍵盤等。
是否需要綁定(如果設置了綁定位配對的)。
是否需要MITM,是否使用OOB等
這些信息會讓BLE協議棧確定一種認證方式:
比如:
1:如果兩端設備的輸入輸出能力有限,比如都沒有鍵盤和顯示器,認證方式就是just work,這其實就是沒有認證,
2:如果兩端設備一個有顯示頻,而另一個有鍵盤,而配對中設置了MITM保護。那麼認證方式就是passkey entery。
一端會顯示一個配對碼,另一需要輸入這個配對碼。之後的配對才能正確進行下去。
3:如果設置了OOB,那麼這個配對碼就是通過另外的通信方式(如NFC)來發送的,而不是像上面一樣一端顯示一端輸入。
這一講的密碼設置就是第二種情況。顯示的密碼是可以隨機的也可以是靜態的。由於設備並沒有顯示器。但是我們仍然可以設置輸入輸出能力為有顯示器,因為我們使用的是靜態密碼。
配對的過程不僅只是輸入配對碼這樣,後續還會根據輸入的配對碼,以及兩端設備交換的隨機數來生成鏈路密鑰來加密鏈路以及分配後續的長期密鑰,身份解析密鑰等需要的密鑰
配對相關的理論比較多,上面的描述只是一個大概的過程。配對過程的詳細介紹在藍牙規范的 安全章節中。
根據上面的理論描述,我們來總結一下:
我們需要的輸入“密碼”這個功能,其實是配對過程中的一部分。而配對過程又是需要首先交換配對信息,然後協議棧會根據交換的信息才決定是否有輸入密碼這一過程。
那麼我們要做的有如下幾步:
1: 首先設置要輸入的靜態密碼
2: 設置配對時會交換的信息:根據上面的介紹如果我們需要手機輸入密碼,那麼配對時就要設置只具有顯示器(這樣就會是一端顯示,
一端輸入,雖然我們真的沒顯示器,但是設置的是靜態密碼所以也是可以的),設置需要MITM攻擊保護。
3:觸發配對。
下面先介紹如何設置靜態密碼:
//首先定義一下靜態密碼,配對密碼只能是 6-digit ASCII string
#defineSTATIC_PASSKEY "123456" /**< Static pin. */
//改結構體中可以設置靜態密碼
staticble_opt_t m_static_pin_option;
定義了這兩個參數後,我們需要設置一下靜態密碼,設置的操作需要在協議棧初始化之後 所以我們將設置密碼操作放在 gap_params_init()函數的最後
如下:
static void gap_params_init(void)
{
//前面都是設置一些設備名以及一些後續需要協商的連接參數
//詳細解釋在 串口透傳剖析 中有說明
uint32_t err_code;
ble_gap_conn_params_tgap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code=sd_ble_gap_device_name_set(&sec_mode,
(constuint8_t*DEVICE_NAME,trlen(DEVICE_NAME));
APP_ERROR_CHECK(err_code);
memset(&gap_conn_params, 0,sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
gap_conn_params.slave_latency= SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout= CONN_SUP_TIMEOUT;
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
//以下是設置靜態密碼操作
uint8_tpasskey[] = STATIC_PASSKEY; m_static_pin_option.gap_opt.passkey.p_passkey= passkey;
//該系統調用執行密碼的設置操作。
err_code=sd_ble_opt_set(BLE_GAP_OPT_PASSKEY,&m_static_pin_option)
APP_ERROR_CHECK(err_code);
}
到這裡設置靜態密碼的操作就做完了。
然後是設置配對時要交換的信息:
下面定義我們需要交換的信息的宏,也就是和安全參數相關的一些宏。
//這裡只是演示靜態密碼,不需要綁定
#define SEC_PARAM_BOND 0
//因為要輸入密碼,就是一種MITM攻擊保護,所以這裡設置MITM
#define SEC_PARAM_MITM 1
//這裡設置只有顯示屏(其實沒有,但是我們用的是事先知道的靜態密碼所以不// 需要顯示)
#define SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_DISPLAY_ONLY
//不使用帶外數據
#define SEC_PARAM_OOB 0
//鏈路加密密鑰的長度
#define SEC_PARAM_MIN_KEY_SIZE 7
#define SEC_PARAM_MAX_KEY_SIZE 16
定義了宏之後我們需要在設置參數,寫一個如下的函數。
m_sec_params 是一個全局變量
ble_gap_sec_params_t m_sec_params;
static void sec_params_init(void)
{
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm =SEC_PARAM_MITM;
m_sec_params.io_caps =SEC_PARAM_IO_CAPABILITIES;
m_sec_params.oob =SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
}
將該函數放在 main函數的初始化流程中的conn_params_init(); 函數之後。
設置的這個全局變量會在配對啟動後的信息交換中使用(因為其內部值就是要交換的信息)。
到這裡我們設置完了配對啟動後會交換的信息。但是怎麼把這個信息給對端設備呢? 先看完最後一步的觸發配對的問題,再來解決將配對信息發給對端設備的問題。
最後一步觸發配對:
配對的觸發有以下幾種情況:
1:主機直接發起。
2:從機發起安全請求,如果之前綁定過,那麼主機會直接用用保存的LTK加密鏈路,如果沒有那麼主機會發起配對請求。
3:BLE中的有一個安全模式的概念。當某個屬性被設置為需要認證的加密鏈路訪問時,那麼當在主機訪問從機的屬**器時,如果鏈路是不安全的就會返回錯誤,然後主機會發起配對請求從而實現安全要求。
我們采用的就是第三種 被動等待主機觸發的方式,那麼首先要做的就是將一些屬性設置為需要安全的鏈路才能訪問,那麼手機在訪問時就會觸發配對過程了。
因為我們是基於 9.0SDK 下的uartdemo,所以我們將具有notify 性質RX 特征值的 cccd(客戶端配置描述符)設置為需要認證和加密的安全鏈路。
因為手機端使能notify是需要寫CCCD的
那麼當手連上板子後 點擊rx特征值的notify 按鈕後主機會發一個 寫命令寫板子上的rx特征值的cccd,因為初試鏈路是不完全的,那麼這時手機就會返回寫出錯,然後啟動配對過程。
設置如下:
在添加RX特征值的函數中做如下的簡單就可以了。
這裡只截取部分代碼:
static uint32_t rx_char_add(ble_nus_t * p_nus, constble_nus_init_t * p_nus_init)
{
/**@snippet [Addingproprietary characteristic to S110 SoftDevice] */
ble_gatts_char_md_tchar_md;
ble_gatts_attr_md_tcccd_md;
ble_gatts_attr_t attr_char_value;
ble_uuid_t ble_uuid;
ble_gatts_attr_md_tattr_md;
memset(&cccd_md, 0, sizeof(cccd_md));
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
//BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
//將上面的一行修改成下面這行
BLE_GAP_CONN_SEC_MODE_SET_ENC_WITH_MITM(&cccd_md.write_perm);
cccd_md.vloc =BLE_GATTS_VLOC_STACK;
memset(&char_md, 0, sizeof(char_md));
··············
··············
············
}
這樣當對端設備(如手機)使能開發板的上rx特征值的notify功能時,就會
因為沒有寫權限而觸發配對,手機會發來配對請求,然後板子回復配對信息,
怎麼回復? 這就是第二步中最後留下的問題。如何將配對信息交給對端設
備(手機)。
當手機發來配對請求時,這對板子來說是一個事件,即配對事件。最終由
dispatch派發函數交給各個服務或模塊的事件處理函數。
那麼我們要做的就是在收到這個配對請求事件後回復第二步中設置的配對
信息就可以了。在main.c 文件中的的on_ble_evt做如下修改
staticvoidon_ble_evt(ble_evt_t * p_ble_evt)
{
uint32_terr_code;
switch(p_ble_evt->header.evt_id)
{
caseBLE_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;
break;
caseBLE_GAP_EVT_DISCONNECTED:
err_code= bsp_indication_set(BSP_INDICATE_IDLE);
APP_ERROR_CHECK(err_code);
m_conn_handle= BLE_CONN_HANDLE_INVALID;
break;
caseBLE_GAP_EVT_SEC_PARAMS_REQUEST:
//注釋掉原本的不支持配對的函數,改為如下的配對回復函數
//err_code= sd_ble_gap_sec_params_reply(m_conn_handle,
//BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
err_code=sd_ble_gap_sec_params_reply(m_conn_handle,
BLE_GAP_SEC_STATUS_SUCCESS,&m_sec_params,NULL);
APP_ERROR_CHECK(err_code);
break;
caseBLE_GATTS_EVT_SYS_ATTR_MISSING:
// No system attributes have beenstored.
err_code=sd_ble_gatts_sys_attr_set(m_conn_handle,
NULL, 0, 0);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
到這裡所有需要配置的都設置完了。程序運行後。手機連接上板子,然後訪問rx特征值。因為該特征值是用來將板子數據通過Notify方式傳給手機的,那麼首先要點擊手機上的notify按鈕去使能板子的notify功能。當我們點擊該按鈕時就會彈出輸入密碼的配對框。