導(dǎo)讀:什么是低功耗藍(lán)牙配對?什么又是綁定?配對和綁定有什么區(qū)別?配對有什么好處?如何刪除綁定信息?如何確定配對的安全等級?just work 的配對一定就不安全嗎?如何開發(fā)自己的配對應(yīng)用?本文將對以上問題進(jìn)行論述。
Paring(配對)和 bonding(綁定)是實現(xiàn)藍(lán)牙射頻通信安全的一種機制,有兩點需要注意:
一是 paring/bonding 實現(xiàn)的是藍(lán)牙鏈路層的安全,對應(yīng)用來說完全透明,也就是說,不管有沒有 paring/bonding,你發(fā)送或接收應(yīng)用數(shù)據(jù)的方式是一樣的,不會因為加了 paring/bonding 應(yīng)用數(shù)據(jù)傳輸需要做某些特殊處理;
二安全有兩種選項:加密或者簽名,目前絕大多數(shù)應(yīng)用都是選擇加密,后續(xù)我們也會以加密為重點進(jìn)行講述
實現(xiàn)藍(lán)牙通信安全,除了 paring/bonding 這種底層方式,用戶也可以在應(yīng)用層去實現(xiàn)相同功能,兩者從功能上和安全性上沒有本質(zhì)區(qū)別,只不過應(yīng)用層自己實現(xiàn)的話,需要自己選擇密碼算法,密鑰生成,密鑰交換等,如果你不是這方面的專家,你的應(yīng)用就有可能會存在安全漏洞。Paring/bonding 則把上述過程標(biāo)準(zhǔn)化,放在了藍(lán)牙協(xié)議棧中,并且其安全性得到了充分評估,用戶可以 “無感的” 用上安全的藍(lán)牙通信。
Paring/bonding 是藍(lán)牙 security manager(SM)的一部分,SM 定義了藍(lán)牙通信的安全框架,里面涉及安全架構(gòu),密碼工具箱,paring 協(xié)議等,其中 paring 協(xié)議是關(guān)鍵,所以我們經(jīng)常把 paring 和 SM 二者等價,下面將對 paring 進(jìn)行詳細(xì)闡述。
1、基本概念解讀
°Paring(配對)
配對包括配對能力交換,設(shè)備認(rèn)證,密鑰生成,連接加密以及機密信息分發(fā)等過程,配對的目的有三個:加密連接,認(rèn)證設(shè)備,以及生成密鑰。從手機角度看,一旦設(shè)備跟手機配對成功,藍(lán)牙配置菜單將包含該配對設(shè)備,如下所示:
如果用戶需要主動刪除配對設(shè)備,點擊配對設(shè)備右邊的“設(shè)置”菜單,出現(xiàn)如下界面,選擇“取消配對”或者“忽略該設(shè)備”,設(shè)備的配對信息即被手機刪除。
°
Bonding(綁定)
配對過程中會生成一個長期密鑰(LTK,long-term Key),如果配對雙方把這個 LTK 存儲起來放在 Flash 中,那么這兩個設(shè)備再次重連的時候,就可以跳過配對流程,而直接使用 LTK 對藍(lán)牙連接進(jìn)行加密,設(shè)備的這種狀態(tài)稱為 bonding。
如果 paring 過程中不存儲 LTK(不分發(fā) LTK)也是可以的,paring 完成后連接也是加密的,但是如果兩個設(shè)備再次重連,那么就需要重走一次 paring 流程,否則兩者還是明文通信。
在不引起誤解的情況下,我們經(jīng)常把 paring 當(dāng)成 paring 和 bonding 兩者的組合,因為只 paring 不 bonding 的應(yīng)用情況非常少見。在不引起混淆的情況下,下文就不區(qū)分 paring 和 bonding 的區(qū)別,換句話說,我們會把 paring 和 bonding 兩個概念等同起來進(jìn)行混用。
°SM(security manager)
藍(lán)牙協(xié)議棧的安全管理層,規(guī)定了跟藍(lán)牙安全通信有關(guān)的所有要素,包括 paring,bonding,以及下文提到的 SMP。
°SMP(security manager protocol)
安全管理協(xié)議,SMP 著重兩個設(shè)備之間的藍(lán)牙交互命令序列,對 paring 的空中包進(jìn)行了嚴(yán)格時序規(guī)定。
°OOB(out of band,帶外)
OOB 就是不通過藍(lán)牙射頻本身來交互,而是通過比如人眼,NFC,UART 等帶外方式來交互配對信息,在這里人眼,NFC,UART 通信方式就被稱為 OOB 通信方式。
°Passkey
又稱 pin 碼,是指用戶在鍵盤中輸入的一串?dāng)?shù)字,以達(dá)到認(rèn)證設(shè)備的目的。低功耗藍(lán)牙的 passkey 必須為 6 位。
°Numeric comparison(數(shù)字比較)
Numeric comparison 其實跟 passkey 一樣,也是用來認(rèn)證設(shè)備的,只不過 passkey 是通過鍵盤輸入的,而 numeric comparison 是顯示在顯示器上的,numeric comparison 也必須是 6 位的數(shù)字。
°MITM(man in the middle)
MITM 是指 A 和 B 通信過程中,C 會插入進(jìn)來以模擬 A 或者 B,并且具備截獲和篡改 A 和 B 之間所有通信報文的能力,從而達(dá)到讓 A 或者 B 信任它,以至于錯把 C 當(dāng)成 B 或者 A 來通信。
如果對安全要求比較高,需要具備 MITM 保護(hù)能力,在 SM 中這個是通過認(rèn)證(authentication)來實現(xiàn)的,SM 中實現(xiàn)認(rèn)證的方式有三種:OOB 認(rèn)證信息,passkey 以及 numeric comparison,大家根據(jù)自己的實際情況,選擇其中一種即可。
°LESC(LE secure connections)
又稱 SC,藍(lán)牙 4.2 引入的一種新的密鑰生成方式和驗證方式,SC 通過基于橢圓曲線的 Diffie-Hellman 密鑰交換算法來生成設(shè)備 A 和 B 的共享密鑰,此密鑰生成過程中需要用到公私鑰對,以及其他的密碼算法庫。
LESC 同時還規(guī)定了相應(yīng)的通信協(xié)議以生成該密鑰,并驗證該密鑰。需要注意的是 LESC 對 paring 的其他方面也會產(chǎn)生一定的影響,所以我們經(jīng)常會把 LESC 看成是一種新的配對方式。
°Legacy paring
在 LESC 引入之前的密鑰生成方式,稱為 legacy paring,換句話說,legacy paring 是相對 LESC 來說的,不支持 LESC 的配對即為 legacy paring(legacy 配對)。
°TK(Temporary Key,臨時密鑰)
legacy paring 里面的概念,如果采用 just work 配對方式,TK 就是為全 0;如果采用 passkey 配對方式,TK 就是 passkey;如果采用 OOB 配對方式,TK 就是 OOB 里面的信息。
°STK(short term key,短期密鑰)
legacy 配對里面的概念,STK 是通過 TK 推導(dǎo)出來的,通過 TK 對設(shè)備 A 和 B 的隨機數(shù)進(jìn)行加密,即得到 STK。
°LTK(long term key,長期密鑰)
legacy 配對和 LESC 配對都會用到 LTK,如前所述,LTK 是用來對未來的連接進(jìn)行加密和解密用的。Legacy paring 中的 LTK 由從設(shè)備根據(jù)相應(yīng)的算法自己生成的(LTK 生成過程中會用到 EDIV(分散因子)和 Rand(隨機數(shù))),然后通過藍(lán)牙空中包傳給主機。
LESC 配對過程中,先通過 Diffie-Hellman 生成一個共享密鑰,然后這個共享密鑰再對設(shè)備 A 和 B 的藍(lán)牙地址和隨機數(shù)進(jìn)行加密,從而得到 LTK,LTK 由設(shè)備 A 和 B 各自同時生成,因此 LTK 不會出現(xiàn)在 LESC 藍(lán)牙空中包中,大大提高了藍(lán)牙通信的安全性。
°IRK(Identity Resolving Key,藍(lán)牙設(shè)備地址解析密鑰)
有些藍(lán)牙設(shè)備的地址為可解析的隨機地址,比如 iPhone 手機,由于他們的地址隨著時間會變化,那如何確定這些變化的地址都來自同一個設(shè)備呢?
答案就是 IRK,IRK 通過解析變化的地址的規(guī)律,從而確定這些地址是否來自同一個設(shè)備,換句話說,IRK 可以用來識別藍(lán)牙設(shè)備身份,因此其也稱為 Identity information。IRK 一般由設(shè)備出廠的時候按照一定要求自動生成。
°Identity Address(設(shè)備唯一地址)
藍(lán)牙設(shè)備地址包括 public,random static, private resolvable,random unresolved 共四類。
如果設(shè)備不支持 privacy,那么 identity address 就等于 public 或者 random static 設(shè)備地址。
如果設(shè)備支持 privacy,即使用 private resolvable 藍(lán)牙設(shè)備地址,在這種情況下,雖然其地址每隔一段時間會變化一次,但是 identity address 仍然保持不變,其取值還是等于內(nèi)在的 public 或者 random static 設(shè)備地址。
Identity Address 和 IRK 都可以用來唯一標(biāo)識一個藍(lán)牙設(shè)備。
°IO capabilities(輸入輸出能力)
是指藍(lán)牙設(shè)備的輸入輸出能力,比如是否有鍵盤,是否有顯示器,是否可以輸入 Yes/No 兩個確認(rèn)值。
°Key size(密鑰長度)
一般來說,密鑰默認(rèn)長度為 16 字節(jié),為了適應(yīng)一些低端的藍(lán)牙設(shè)備處理能力,你也可以把密鑰長度調(diào)低,比如變?yōu)?10 個字節(jié)。
2、Paring 流程及命令
°Paring 包含三個階段:1、階段 1:配對特性交換,即交換各自都支持哪些配對特性,比如支不支持 SC,支不支持 MITM,支不支持 OOB,以及它的輸入輸出能力等。2、階段 2:密鑰生成階段,legacy paring 和 LESC paring 兩者的區(qū)別就在這里,因此后續(xù)我們會分開闡述 legacy paring 和 SC paring 的階段 2。
Legacy paring:STK 生成(注:legacy paring 的 LTK 生成跟配對流程無關(guān),如前所述,其是由從機自己生成的)
SC paring:LTK 生成
3、階段 3:通過藍(lán)牙空中包分發(fā)一些秘密信息。Legacy paring 需要分發(fā) LTK,IRK 等,而 SC paring 只需分發(fā) IRK。秘密信息分發(fā)之前,必須保證連接已加密。 °Paring 流程如下所示:
2.1 階段 1:配對特性交換 Paring 流程及命令
°配對特性交換涉及三條 PDU 命令:1、Paring_Request:
2、Paring_Response:
3、Security_Request:
°IO Capability 占一個字節(jié),其定義如下所示:
°AuthReq 也是占用一個字節(jié),其定義如下所示:
2.2 階段 2:密鑰生成
°根據(jù)階段 1 的 IO 輸入輸出能力以及是否存在 OOB,階段 2 存在如下幾種配對方式(或者說認(rèn)證方式):
Just works
Numeric comparison(LESC 才有)
Passkey
OOB
對于 legacy paring,如果 A 和 B 都支持 OOB,那么兩者就會采用 OOB 方式進(jìn)行配對,否則根據(jù) IO 能力選擇配對方式。對于 SC paring,如果 A 或者 B 有一方支持 OOB,那么兩者就會采用 OOB 方式進(jìn)行配對,否則根據(jù) IO 能力選擇配對方式。不同的 IO 能力對應(yīng)的配對方式如下所示。
粗略來說,有認(rèn)證的配對方式就具備 MITM 保護(hù)功能,從 IO 角度看,有三種配對方式:just works,passkey 和 Numeric Comparison,其中 just works 沒有 MITM 保護(hù)功能,而 passkey 和 Numeric comparison 具備 MITM 保護(hù)功能。換句話說,如果你要求你的設(shè)備具備 MITM 保護(hù)功能,那么它必須有一定 IO 能力,而不能是“NoInputNoOutput”。至于 OOB 方式有沒有 MITM 保護(hù),取決于 OOB 通信的安全性,如果 OOB 通信具備 MITM 保護(hù),那么藍(lán)牙也具備 MITM 保護(hù),否則就不具備。
下面分 legacy paring 和 sc paring 對配對流程進(jìn)行講解。
2.2.1 legacy paring
°Legacy paring 整個配對流程是圍繞 STK 生成來做的: 設(shè)備的認(rèn)證是通過設(shè)備 A 和 B 經(jīng)由 TK 生成一個確認(rèn)數(shù),如果這個確認(rèn)數(shù)相同,則認(rèn)證通過。 如前所述,legacy paring 需要先生成 TK,TK 的生成方式取決于配對方式:
Just works。TK 默認(rèn)為全 0
Passkey。TK 由 6 位 passkey 擴展而來
OOB。TK 直接由 OOB 數(shù)據(jù)提供
°然后生成確認(rèn)數(shù),算法如下所示:
°生成 STK 的算法如下所示:
°以 passkey legacy paring 為例,其第 2 階段全工作流程如下所示:
Just works 和 OOB 配對流程就不再贅述了,大家自己去看一下藍(lán)牙核心規(guī)范的說明。
這里強調(diào)一下,配對完成之后,連接就會加密,而且加密的密鑰是 STK,而不是 LTK。
對于 legacy paring,如果 A 和 B 都支持 OOB,那么兩者就會采用 OOB 方式進(jìn)行配對,否則根據(jù) IO 能力選擇配對方式。對于 SC paring,如果 A 或者 B 有一方支持 OOB,那么兩者就會采用 OOB 方式進(jìn)行配對,否則根據(jù) IO 能力選擇配對方式。不同的 IO 能力對應(yīng)的配對方式如下所示。
2.2.2 LESC paring
°跟 legacy paring 不一樣的地方: LESC paring 是通過 Diffie-Hellman 算法直接生成 LTK,因此它不需要生成 TK 和 STK。為了生成 LTK,雙方需要先交換公鑰,流程如下所示:
公鑰交換后,設(shè)備 A 和 B 就開始獨自計算各自的 DHKey,按照 D-H 算法,他們倆算出的 DHKey 會是同一個。而 LTK 和 MacKey 就是通過這個 DHKey 加密一系列數(shù)據(jù)而得到的。
Legacy paring 在整個配對流程中只做一次認(rèn)證,而 LESC paring 會做兩次認(rèn)證。LESC 第一階段認(rèn)證的原理是,設(shè)備 A 和 B 各生成一個隨機數(shù),然后認(rèn)證這個隨機數(shù)對不對。LESC 第二階段認(rèn)證過程是:設(shè)備 A 和 B 通過 MacKey 各生成一個檢查值,對方確認(rèn)這個值對不對。
以 LESC Numeric comparison 為例,其第一階段認(rèn)證流程如下所示:
我們還是以 LESC Numeric comparison 為例,其第二階段全工作流程如下所示:
一旦 LTK 生成成功,主機端就可以發(fā)起加密連接流程,如下所示:
至此,LESC 連接被 LTK 加密了,后面就可以分發(fā)秘密信息了。
2.3 階段 3:秘密信息分發(fā)
°一旦連接加密了,主機和從機之間就可以分發(fā)一些秘密信息。如果是 legacy paring,如下秘密信息必須分發(fā):
LTK
EDIV
Rand
°同時根據(jù)情況,legacy paring 還需分發(fā)如下信息:
IRK
Identity adresss
°對于 LESC paring,秘密信息分發(fā)是可選,一般有可能分發(fā)如下信息:
IRK
Identity adresss
如下為 legacy paring 可能分發(fā)的最多秘密信息的一個例子:
2.4 綁定,重連和加密
°如果配對的兩個設(shè)備生成了 LTK 及其他秘密信息: 如上所述,如果配對的兩個設(shè)備生成了 LTK 及其他秘密信息,并且把 LTK 及其他秘密信息保存到 Flash 等永久化存儲設(shè)備中,那么我們就可以說這兩個設(shè)備綁定成功。換句話說,paring 和 bonding 是兩個不同的概念,paring 更強調(diào)認(rèn)證和密鑰生成,而 bonding 更強調(diào)密鑰保存。一旦兩個設(shè)備 bonding 成功,那么這兩個設(shè)備斷開再次重連的時候,主機就可以發(fā)起加密流程,從而使用 paring 生成的 LTK 對后續(xù)的連接進(jìn)行加密。主機發(fā)出加密連接流程如下所示:
這里說明一下,加密連接只能由主機發(fā)出,而不能由從機發(fā)起。不過從機可以發(fā)出加密請求,主機收到從機的加密請求后,可以發(fā)起加密連接也可以拒絕其請求。如下為主機同意從機的加密請求的工作流程:
2.5 配對命令一覽表
°如下為 SM 中用的 PDU 命令列表: (注:加密連接命令屬于 LL 控制命令,所以沒有包含在其中)
3. Nordic SDK 配對流程
°如下為 SM 中用的 PDU 命令列表: 那么如何實現(xiàn)這個配對流程呢?也就是說,我該調(diào)用哪些 API 去實現(xiàn)配對流程,這些 API 調(diào)用的順序又是如何,具體會產(chǎn)生哪些協(xié)議棧事件,該如何處理這些協(xié)議棧事件,這就涉及到協(xié)議棧的實現(xiàn)。
Nordic 藍(lán)牙協(xié)議棧 softdevice 提供詳細(xì)的工作流程圖,以指導(dǎo)用戶如何調(diào)用 softdevice API 去實現(xiàn)想要的配對流程,詳細(xì)的配對流程圖請參考 infocenter 如下界面:
比如 S132 協(xié)議棧,其從機端配對流程圖鏈接為:https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s132.api.v7.0.1%2Fgroup___b_l_e___g_a_p___p_e_r_i_p_h___s_e_c___m_s_c.html。
以 legacy paring,從機端顯示 passkey,主機端輸入 passkey 為例,softdevice 的配對流程圖如下所示,鏈接為:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s132.api.v7.0.1%2Fgroup___b_l_e___g_a_p___p_e_r_i_p_h___b_o_n_d_i_n_g___p_k___p_e_r_i_p_h___m_s_c.html
上述配對流程圖把用到的 API,產(chǎn)生的 softdevice 事件,以及 softdevice 事件如何處理,都一一闡明,大家只要按照這個流程圖來做,就可以完成期望的配對。更讓人省心的是,Nordic SDK 已經(jīng)把幾種典型的配對場景做成了例子,大家可以直接就拿過來用,連上面的配對流程圖都不用看,就可以輕松完成自己的配對應(yīng)用。
Nordic 提供的配對例子有:ble_app_hids_keyboard,ble_app_hrs,ble_app_gls,ble_app_bps,ble_app_bms,ble_app_cscs,ble_app_hrs_nfc_pairing,experimental_ble_app_hrs_nfc_pairing,ble_app_hids_keyboard_pairing_nfc,ble_app_multirole_lesc 等,基本上囊括了藍(lán)牙各種配對情況。
后面會以 ble_app_hrs 為例來詳細(xì)講解如何實現(xiàn)低功耗藍(lán)牙配對
4. 配對例程 ble_app_hrs 解讀
°nRF5 SDK 把藍(lán)牙配對做成了一個模塊: peer_manager,也就是說,所有關(guān)于 paring 的工作都由 peer manager 自動完成,用戶無需去了解 softdevice 底層 API 的使用方法,大家直接參考 nRF5 SDK 里面的例程就可以完成自己的配對應(yīng)用開發(fā)。
nRF5 SDK 提供的配對例子有:ble_app_hids_keyboard,ble_app_hrs,ble_app_gls,ble_app_bps,ble_app_bms,ble_app_cscs,ble_app_hrs_nfc_pairing,experimental_ble_app_hrs_nfc_pairing,ble_app_hids_keyboard_pairing_nfc,ble_app_multirole_lesc 等,基本上囊括了藍(lán)牙各種配對情況,下面將和大家一起來解讀 ble_app_hrs 配對相關(guān)代碼
如果你對 Nordic nRF5 SDK 和 softdevice 不是很熟的話,建議你先看一下這篇文章:手把手教你開發(fā) BLE 數(shù)據(jù)透傳應(yīng)用程序 ,以建立 Nordic 開發(fā)的一些基礎(chǔ)知識,然后再往下看。 跟沒有 paring 的 ble 應(yīng)用代碼相比,有 paring 的 ble 應(yīng)用只多了一個初始化函數(shù):peer_manager_init(),peer_manager_init 實現(xiàn)代碼如下所示:
peer_manager_init 里面注冊了一個回調(diào)函數(shù):pm_evt_handler,用來添加一些用戶自定義的處理,例子代碼 pm_evt_handler 的實現(xiàn)如下所示:
至此,一個 just works 的藍(lán)牙配對例子就算完成了,是不是有點懵?感覺太簡單了以至于有點接受不了。沒關(guān)系,下面我們在這個例子上加一些額外的功能,以加深大家對它的理解。
5. 改變 ble_app_hrs 配對方式
°把 ble_app_hrs 配對方式改成 LESC with numeric comparison: 原始 ble_app_hrs 為 just work 方式的 LESC 配對,我們現(xiàn)在把它改成最高安全級別的 numeric comparison LESC。我們的開發(fā)板沒有顯示器,因此我們將通過日志的方式把數(shù)字比較值輸出,同時把 button3 的按下作為 yes 確認(rèn),button4 的按下作為 reject 確認(rèn)。
何實現(xiàn) numeric comparison?前面我也提過,如果 SDK 有現(xiàn)成的例子,直接參考例子來;如果 SDK 沒有現(xiàn)成的例子,那么就參考 softdevice 工作時序圖。關(guān)于 LESC numeric comparison,從機端的工作流程如下所示:
https://infocenter.nordicsemi.com/index.jsp?
topic=%2Fcom.nordic.infocenter.s132.api.v7.0.1%2Fgroup___b_l_e___g_a_p___p_e_r_i_p_h___l_e_s_c___b_o_n_d_i_n_g___n_c___m_s_c.html
這里要強調(diào)一下,時序圖會把有可能需要用到事件和 API 都列出來,但不意味著列出來的事件和 API 都需要你那哪些事件和 API 需要用戶自己處理呢?一個原則:全文搜索一下,只要 peer manager 已經(jīng)調(diào)用過的 API,你就不用處理;而流程圖中剩下的 API 就需要你自己去處理了。
比如上面這個例子,sd_ble_gap_sec_params_reply 已經(jīng)被 peer manager 模塊處理了,所以你不用處理;而 BLE_GAP_EVT_PASSKEY_DISPLAY 和 sd_ble_gap_auth_key_reply 只在 passkey 和 numeric comparison 配對方式中才會出現(xiàn),peer manager 沒有對其進(jìn)行處理,因此需要用戶自己處理。為此,我們在 ble_evt_handler 中加上分支:BLE_GAP_EVT_PASSKEY_DISPLAY,并按照流程圖的要求加上相應(yīng)的處理,代碼如下所示:
上面只是顯示了 passkey,如前所述,如果 button3 按下我們回復(fù) BLE_GAP_AUTH_KEY_TYPE_PASSKEY;如果 button4 按下我們回復(fù) BLE_GAP_AUTH_KEY_TYPE_NONE。相關(guān)代碼如下所示:
如前所述,配對方式是由 IO 輸入輸出能力確定的,而且 numeric comparison 是具備 MITM 能力的,為此我們還需要修改如下兩個地方:
蘋果手機是不能手動發(fā)起配對請求的,為了讓蘋果手機自動發(fā)起配對請求,我們將如下 characteristic 的安全級別提高:(注:除了這種方法,我們也可以通過從機主動發(fā)起安全請求來達(dá)到同樣的目的)。
我這里以 PCA10040/Keil5 工程為例來編譯,請編譯工程:
nRF5SDK160098a08e2examplesle_peripheralle_app_hrspca10040s132arm5_no_packs。
將編譯好的代碼下載到開發(fā)板中,測試的時候,我們先連接開發(fā)板,然后使能 CCCD,此時不管 Android 手機還是蘋果手機,都會跳出配對對話框,同時顯示出配對碼,如下:
開發(fā)板也把配對碼打印出來了,如果兩者一致,按下 button3,整個配對流程順利完成,開發(fā)板會打印如下信息:
上述代碼已上傳到百度云盤,大家可以去百度云盤下載:
代碼鏈接
鏈接:https://pan.baidu.com/s/1FKTfY3Q_zBVvviO7KC7Gyg#list/path=%2Fblog
密碼:y8fb
ble_app_hrs_nc.rar,然后解壓縮到 SDK 根目錄 examplesble_peripheral,打開 Keil5 工程:
SDK 根目錄 examplesble_peripheral ble_app_hrs_ncpca10040s132arm5_no_packs,就可以直接編譯和運行。
6. 關(guān)于配對的一些小貼士
°蘋果手機的一點不同: 安卓手機允許用戶手動發(fā)起 paring 請求,而蘋果手機則沒有這個功能。因此,即使你的 characteristic 沒有使能安全級別,安卓手機還是可以跟你的設(shè)備完成配對的,而蘋果手機則不支持這個功能,蘋果手機要不要跟設(shè)備進(jìn)行配對,不能由人來控制的,只能由蘋果 iOS 來控制。 欲觸發(fā)蘋果 iOS 發(fā)起配對請求,有兩種方法:
一是將某個 characteristic 加上安全認(rèn)證權(quán)限,這樣 iOS 在服務(wù)發(fā)現(xiàn)過程中就會自動發(fā)起配對請求,以滿足 characteristic 的安全認(rèn)證級別;
二是從機端主動發(fā)起安全請求,iOS 收到從機的安全請求后,會等待用戶的授權(quán)確認(rèn)從而發(fā)起配對請求。這兩種方法在 ble_app_gls 中都有體現(xiàn),大家可以參考相關(guān)代碼。
°重連加密等級: 綁定成功后,如果發(fā)生重連,那么主機應(yīng)該自動發(fā)起加密連接請求,以對連接進(jìn)行加密。一般來說,在連接沒有成功加密前,主從機不要做敏感數(shù)據(jù)的交互,否則 softdevice API 會報 NRF_ERROR_FORBIDDEN。對于有 MITM 保護(hù)的加密連接,在收到 PM_EVT_CONN_SEC_SUCCEEDED 這個事件后,設(shè)備應(yīng)該去檢測連接的安全等級是否符合要求,具體可參考 ble_app_gls 例子的做法。 °Service changed(服務(wù)改變): 設(shè)備跟手機綁定成功后,手機再次重連這個設(shè)備時,就會自動跳過 service discovery 過程,換句話說,配對的時候手機會把設(shè)備所有服務(wù)和 characteristic 的 handle 保存下來,二次重連的時候,直接用以前保存的 handle 值去操作設(shè)備。
但是,如果設(shè)備的服務(wù)改變了,此時手機再用之前的 handle 去操作設(shè)備,就會出問題。為了解決這個問題,在 GATT 主服務(wù)里面引入了 service changed characteristic,如下所示:
有了這個 characteristic,當(dāng)設(shè)備的服務(wù)發(fā)生改變時,設(shè)備就可以通過這個 characteristic 發(fā)送一個 indicate PDU 給到手機,從而手機知道設(shè)備的服務(wù)已發(fā)生了改變,此時手機會重新發(fā)起 service discovery 流程,以重新獲得 service 和 characteristic 最新的 handle 列表。欲添加 service changed characteristic,你只需在 sdk_config.h 文件中打開如下兩個宏:
然后當(dāng)服務(wù)發(fā)生改變時,調(diào)用 pm_local_database_has_changed(),協(xié)議棧就會自動發(fā)起 service changed indicate PDU 給手機,從而引起手機重走服務(wù)發(fā)現(xiàn)過程。
°刪除主機端綁定信息: 如果手機端刪除了綁定信息,為了安全起見,設(shè)備端也需要跟著一起刪除綁定信息,否則手機無法再次跟設(shè)備進(jìn)行配對,這個是最理想的情況,但是我們有的設(shè)備沒有任何輸入接口,無法手動刪除綁定信息,這個時候能不能有一種辦法可以讓手機跟設(shè)備進(jìn)行二次配對呢?
為此,Nordic 提供了一種 workaround,在藍(lán)牙事件回調(diào)函數(shù)里面,加上如下代碼即可:
if (p_evt->evt_id == PM_EVT_CONN_SEC_CONFIG_REQ)
{
pm_conn_sec_config_t cfg;
cfg.allow_repairing = true;
pm_conn_sec_config_reply(p_evt->conn_handle, &cfg);
}
這樣,即使用戶把手機端 paring 信息刪掉,設(shè)備端 paring 信息沒有刪掉,手機還是可以跟設(shè)備進(jìn)行二次配對的。
°刪除從機端綁定信息: 跟上面相反,如果設(shè)備端 bonding 信息被刪除了,而手機端 bonding 信息沒有被刪除,這種情況下如何實現(xiàn)二次配對?
最安全的方式,讓用戶主動刪除手機端綁定信息,但是很多開發(fā)者希望,用戶體驗好一點,也就是說,碰到這種情況希望手機能自動刪除綁定信息,這個能不能實現(xiàn)跟手機有很大關(guān)系,首先我們確保協(xié)議棧返回 LL_REJECT_IND or LL_REJECT_EXT_IND,錯誤碼為“PIN or key missing”,一般而言,手機收到這個 PDU 后,都會自動刪除 bonding 信息。
如果上述方法行不通的話,那么發(fā)送完 LL_REJECT_IND 后再調(diào)用斷開函數(shù)(sd_ble_gap_disconnect),同時將斷開原因設(shè)為 BLE_HCI_AUTHENTICATION_FAILURE 即可。
°同時綁定多個設(shè)備: Nordic SDK 是支持一個設(shè)備同時跟多個主機綁定,只要設(shè)備存儲空間足夠大,那么可以綁定的設(shè)備數(shù)就不設(shè)限。nRF5 SDK 中 bonding 信息也是通過 fds 來存儲的,也就是說綁定信息和用戶 Flash 數(shù)據(jù)共享同一塊空間,如果需要綁定多個設(shè)備,那么 FDS_VIRTUAL_PAGES 這個宏的值必須進(jìn)行修改,以保證分配的 Flash 空間可以同時容納 bonding 信息和用戶 Flash 數(shù)據(jù)。
一般來說,如果需要綁定多個設(shè)備,請設(shè)置一個最大綁定數(shù),比如 8 個,這樣,一旦檢測到綁定數(shù)達(dá)到 8 了,就可以把以前老的 bonding 設(shè)備刪除,從而節(jié)省存儲空間。那如何知道哪個設(shè)備是老設(shè)備哪個設(shè)備是新設(shè)備?這個是通過 peer rank 來實現(xiàn)的,大家只要使能 PM_PEER_RANKS_ENABLED 這個宏,就可以自動實現(xiàn)排序。
°循環(huán)綁定測試: 很多開發(fā)者喜歡做循環(huán)綁定測試,即同一部手機不斷跟同一個設(shè)備進(jìn)行配對,然后刪除配對信息,然后再進(jìn)行配對,他們測試下來發(fā)現(xiàn):
達(dá)到一定次數(shù)后,設(shè)備就工作不正常了,這個是由于當(dāng) bonding 信息不斷累積而不進(jìn)行刪除的話,那么分配給 fds 的 Flash 空間就會耗盡,從而導(dǎo)致異常出現(xiàn)(最新的 SDK 會在 Flash 存儲空間耗盡時,自動刪除最老設(shè)備的綁定信息,但即使這樣,對用戶 Flash 數(shù)據(jù)的操作影響還是很大)。
解決這個問題的方法就是設(shè)定一個最大 bonding 數(shù),達(dá)到這個數(shù)目后,刪除老 bonding 信息,從而達(dá)到循環(huán)利用 Flash 空間的目的。當(dāng)然如果你的 fds 只是用來存儲 bonding 信息而不做其他用戶數(shù)據(jù)操作的話,那么就沒有必要加上這個功能了。
°白名單與綁定: 雖然白名單和綁定二者沒有任何聯(lián)系,但是我們一般都把兩者結(jié)合起來一起使用,以達(dá)到我們的使用期望。當(dāng)兩個設(shè)備綁定成功后,我們就可以將對方的 mac 地址或者 IRK 放入白名單中,同時開啟白名單廣播,這樣設(shè)備只跟白名單中的主機進(jìn)行連接,白名單以外的設(shè)備在 controller 層面就被過濾掉了,從而提高私密性以及連接效率。
這種情況下,哪怕是合法的設(shè)備,如果之前沒有跟設(shè)備綁定,那么它也無法跟設(shè)備建立連接。換句話說,如果你想把新設(shè)備加入到白名單中,那么首先需要禁止白名單廣播而采用普通廣播,然后跟新設(shè)備進(jìn)行配對,成功后再把新設(shè)備身份信息加入到白名單中。白名單與綁定的例子具體可參考:ble_app_hids_keyboard。
°Authenticated payload timeout: 大家都知道藍(lán)牙連接有一個 supervision timeout 時間,也就是說,當(dāng)建立連接的兩個設(shè)備,任何一方在 supervision timeout(比如 4s)時間內(nèi),沒有給對方發(fā)送任何藍(lán)牙空口包,此時認(rèn)為連接已斷開,并觸發(fā) supervision timeout 事件。
當(dāng)設(shè)備雙方建立加密連接后,不僅有上述的 supervision timeout,還有一個 authenticated payload timeout,authenticated payload timeout 默認(rèn)為 30s,它的意思是,兩個設(shè)備加密后,30s 內(nèi)必須有一個有數(shù)據(jù)的空口包交互,而不能一直發(fā)空包,否則認(rèn)為 authenticated payload timeout。
Authenticated payload timeout 是協(xié)議棧自動管理的,對軟件開發(fā)來說是透明的,每 30s 時間到,如果期間沒有任何有效數(shù)據(jù)包交互(一直在發(fā)空包),協(xié)議棧會自動發(fā)送一個 ping request 給對方,以避免 authenticated payload timeout 的出現(xiàn)(注:這里的協(xié)議棧既可以是設(shè)備的協(xié)議棧,也可以是手機的協(xié)議棧)。
有時候不想等到 30s 超時到了再發(fā)送 ping request,大家可以在 connected 事件中,調(diào)用如下 API 以提前發(fā)出 ping request。
當(dāng)然,如果你能保證每 30s 時間內(nèi),手機和設(shè)備之間肯定會有有效數(shù)據(jù)包交互,或者手機端能及時準(zhǔn)確地發(fā)出 ping request,那么上述過程就完全沒有必要了。
低功耗藍(lán)牙密鑰NFC