一. 低功耗藍牙Bluetooth LE安全必須應對 的3 種常見攻擊類型:
身份追蹤,被動竊聽(嗅探)和主動竊聽(中間人MITM)
身份跟蹤:利用藍牙地址來跟蹤設備。這可以通過使用隨機變化的可解析私有地址(Resolvable Random Private Address)來避免,其中只有綁定/可信設備才能解析私有地址。 IRK(身份解析密鑰)用于生成和解析私有地址。
被動竊聽(嗅探):就是攻擊者偷偷竊聽設備之間的數據傳輸。 可以通過加密對等設備之間的通信來防止這種情況。 這里的挑戰是對等設備如何生成和/或交換密鑰以建立安全地加密連接。 Bluetooth LE 傳統配對的密碼生成和交換存在不足,然后提出了LE secure Connections。
主動竊聽(中間人MITM):在主動竊聽(或中間人)攻擊中,攻擊者冒充合法設備來欺騙另外兩個設備,與他們建立連接。 為了防止這種情況,我們需要確保我們正在通信的設備實際上是我們想要與之通信的設備,而不是未經身份驗證的設備。可靠的身份驗證過程可以有效的防止中間人攻擊。中間人攻擊的示例,如下圖:
二. BLE安全相關術語:
臨時密鑰 (Temporary Key,TK): 臨時密鑰的生成取決于所選的配對方法。每次配對過程發生時都會生成 TK(僅用于傳統配對)。
短期密鑰 (Short Term Key,STK): 此密鑰由設備之間交換的 TK 生成。每次配對過程發生時都會生成 STK,并用于加密當前連接中的數據(僅用于傳統配對)。
長期密鑰 (LongTerm Key,LTK): 此密鑰在傳統配對的第三階段和 LE 安全連接的第二階段期間生成和存儲,是一個 128 位密鑰,用于生成session key。它存儲在綁定的兩個設備中的每一個上,并用于兩個設備之間的后續加密連接。
加密多樣化(Encrypted Diversifier,EDIV)和 隨機值(Random Number,Rand): 這兩個值用于創建和識別 LTK。它們也會在綁定過程中儲存起來。
身份解析密鑰 (ldentity Resolving Key,IRK): 用于生成和解析隨機私有可解析地址(Resolvable Random Private Address)。該密鑰對于每個設備都是唯一的,因此主設備的 IRK 將存儲在從設備端,而從設備的IRK 將存儲在主設備端。
連接簽名解析密鑰(Connection Signature Resolving Key,CSRK): 如果啟用,該密鑰將存儲在兩個綁定設備中的每一個上,用于對數據進行簽名并驗證附加到另一端數據的簽名(used in signing data sent over an unencrypted link )。
ECDH(Elliptic-curve Diffie–Hellman)橢圓曲線diffie-hellman密鑰交換算法
三. 可解析隨機私有地址(Resolvable Random Private Address)
可解析隨機私有地址可以防止被未知設備掃描和追蹤
如圖,由兩部分組成:高位24bits是隨機數部分,其中最高兩個bit為"10",用于標識地址類型;低位24bits是隨機數和IRK經過hash運算得到的hash值,運算的公式為hash = ah(IRK, prand)。
當對端Bluetooth LE設備掃描到該類型的藍牙地址后,會使用保存在本機的IRK,和該地址中的prand,進行同樣的hash運算,并將運算結果和地址中的hash字段比較,相同的時候,才進行后續的操作。這個過程稱作resolve(解析),這也是Non-resolvable private address/Resolvable private address命名的由來。以T_GAP(private_addr_int)為周期,定時更新。哪怕在廣播、掃描、已連接等過程中,也可能改變。
四. Bluetooth LE安全等級
Bluetooth LE 在安全模式 1 中定義了 4 個安全級別:
1 級:無安全性(開放文本,意味著無需身份驗證且無需加密)
2級:未經身份驗證的配對加密
3 級:經過身份驗證的配對加密
4 級:經過身份驗證的 LE 安全連接配對加密(LE Secure Connections)
安全等級1代表沒有加密。安全等級2,3對應傳統配對(Legacy pairing),安全等級4對應LE安全連接(LE Secure Connections)
傳統配對(Legacy pairing) 和LE安全連接(LE Secure Connections)
在藍牙 v4.2 之前,傳統配對是Bluetooth LE中唯一可用的配對方法。它非常簡單,但存在風險,因為用于加密鏈接的短期密鑰 (STK) 很容易被破解。在傳統配對中使用 Just Works 時,TK 設置為 0,這在竊聽或中間人 (MITM) 方面不提供任何保護。攻擊者可以輕松暴力破解 STK 并竊聽連接,而且也沒有辦法驗證設備。
使用密鑰輸入會好一些,因為 TK 現在是一個 6 位數字,由用戶在設備之間傳遞。例如,其中一個設備在其屏幕上顯示該數字,用戶使用鍵盤在其他設備上輸入該數字。不幸的是,攻擊者可以輕松嗅探正在交換的值,然后很容易找出 STK 并解密連接。即使無法直接確定 TK,也可以通過嘗試所有 999999 種組合輕松破解密鑰。
在帶外配對(OOB)中,TK 通過Bluetooth LE以外的其他方式進行交換,例如使用 NFC。此方法支持使用最大 128 位的 TK,從而提高了連接的安全性。交換期間使用的 OOB 通道的安全性也決定了Bluetooth LE 連接的安全性。如果 OOB 通道受到保護以免遭受竊聽和 MITM 攻擊,那么Bluetooth LE鏈路也是如此。
藍牙 SIG 不推薦使用傳統配對,但如果必須使用,請使用 OOB 方式。OOB身份驗證與傳統配對配合使用可被視為安全的方法。
因此,在藍牙 v4.2 中引入了 LE 安全連接(LE Secure Connections)。 LE 安全連接不使用 TK 和 STK,而是使用橢圓曲線 Diffie-Hellman (ECDH) 加密技術來生成公鑰-私鑰對。設備交換公鑰。它們將使用四種配對方法之一(Just Works, Passkey entry, OOB or Numeric Comparison)來驗證對等設備的真實性,并根據 Diffie-Hellman 密鑰和身份驗證數據生成 LTK。
盡管 Just Works 在使用 LE 安全連接時更安全,但它仍然不提供身份驗證,因此不建議將其用作配對方法。密鑰輸入(Passkey entry)配對方式,使用密鑰、ECDH 公鑰和 128 位任意數字來驗證連接。這意味著它比傳統配對要安全得多。使用 OOB 配對仍然是推薦的選項,因為只要 OOB 通道是安全的,它就會提供保護,就像在傳統配對中一樣。
此外,此功能引入了一種稱為數字比較(Numeric Comparison)的新配對方式。雖然它遵循與 Just Works 配對方式相同的程序,但它在最后增加了另一個步驟來防止 MITM 攻擊,即讓用戶根據兩個對等點上顯示的值,進行手動檢查和確認。
對等點之間交換的唯一數據是公鑰。使用 ECDH 公鑰加密使得破解 LTK 變得極其困難,與傳統配對相比,這是一個顯著的改進。
注:盡管大多數設備都支持 LE 安全連接,但仍有一些藍牙 LE 產品僅支持傳統配對。因此,除了 LE 安全連接之外,啟用對傳統配對的支持也是一個好主意,以便在您的應用程序中實現更好的兼容性。
五. SMP安全管理協議
在藍牙核心規范中,有三個主要的架構層:控制器、主機和應用程序,如下圖所示。在主機層,有一個名為安全管理器 (SM) 的模塊,它定義了配對和密鑰分發的方法和協議、相應的安全工具箱,以及安全管理器協議 (SMP)。SMP定義了配對命令幀格式、幀結構和超時限制,用于配對和傳輸特定的密鑰分發。
SMP具體命令如下圖:
六. Bluetooth LE配對過程
配對是為了創建密鑰,然后可以使用這些密鑰來加密鏈接。配對過程中還可能傳輸特定的密鑰和數據,以便雙方共享一些機密信息。這些機密信息可用于在未來重新連接時加密鏈接、驗證簽名或執行隨機地址解析。
Bluetooth LE配對之前需要先建立連接。配對過程可以分為三個過程,前兩個過程是必須的,第三個過程是可選的。
Phase1 Pairing Feature Exchange: 配對特征交換
Phase2 (LE legacy pairing): Short Term Key (STK) Generation: STK密鑰生成
Phase2 (LE Secure Connections): Long Term Key (LTK) Generation: LTK密鑰生成
Phase3 Transport Specific Key Distribution: 傳輸特定密鑰分發(在傳統配對中,LTK 也在該階段被派發)
配對過程如下圖所示:
(1) 配對特征交換(Pairing Feature Exchange)
配對命令
兩個設備之間的配對信息交換是通過配對請求(pairing request)和配對響應(pairing response)數據包完成的。只有主機才能發送配對請求命令,從機發送配對響應命令進行回復。如果從機也想發起配對,那么可以發送安全請求(security request )命令,主機收到這條命令后,可以選擇發送配對請求,也可以拒絕。處理邏輯如下:
主機,從機都可以調用int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)函數來發起配對。這個函數的處理邏輯里面會判斷當前的身份是主機還是從機,從而發送不同的命令。
配對命令格式
Pairing Feature Exchange主要交換I/O能力,是否支持OOB,是否支持綁定,是否支持MITM,是否支持安全連接(SC)等,還有分發密鑰的相關的信息。格式如下:
1)I/O能力包括:
DisplayOnly:僅有顯示能力
DisplayYesNo:有顯示能力,并可選擇“是”或“否”
KeyboardOnly:僅有鍵盤輸入能力
NoInputNoOutput:沒有輸入和輸出能力
KeyboardDisplay:有鍵盤輸入和顯示能力
I/O能力編碼,如下圖:
2)OOB 數據flag如下圖:
3)AuthReq中各個字段的含義如下:
Bonding Flags:指示是否支持綁定
MITM:如果設備需要請求 MITM 保護,則必須設置此位字段。這將啟用設備的身份驗證。
SC:代表安全連接,如果支持安全連接功能,則設置此字段。如果設備支持 LE 安全連接配對,則必須將 SC 字段設置為 1,否則必須將其設置為 0。如果兩個設備都支持 LE 安全連接配對,則必須使用 LE 安全連接配對,否則將使用 LE 傳統配對。
Keypress:僅在 Passkey Entry 協議中使用,其他協議將忽略它。設置此字段后,輸入 passkey 時將使用按鍵通知。這將一次交換一位(one bit)passkey,這是藍牙 4.2 相對于之前的機制(藍牙 4.1 或更早版本)的一項重要增強。藍牙 4.2之前的處理中,整個passkey在一次確認操作中交換。這增強了passkey交換機制,現在在 MITM 攻擊中預測passkey變得更加困難。
CT2:表示加密過程可以使用名為 h7 的安全功能。
RFU:保留以備將來使用。
4)最大加密密鑰大小 (Maximum Encryption Key Size):由于不同設備的處理能力不同,此字節表示設備可以處理的最大密鑰大小。大小可以是 7 到 16 個字節。Nordic支持16個字節。
發起方密鑰分發(Initiator Key Distribution):此字段表示發起方希望在后續階段生成和分發哪些密鑰。
響應方密鑰分發 (Responder Key Distribution):此字段表示發起方希望請求響應方生成和分發哪些密鑰。
密鑰的種類如下:
EncKey代表LTK;IdKey代表IRK ;SignKey代表 CSRK;Bluetooth LE中LinkKey不被分發。
配對模式選擇
通過綜合考慮雙方的OOB標志、MITM標志和I/O能力,來決定使用哪種配對方法。OOB和MITM標志首先確定是直接使用OOB配對方法還是根據I/O能力確定配對方法。如下圖所示:
1) Initiator和Responder兩方都設定了OOB flag的情況下,選用OOB
2) Initiator和Responder一方設定了OOB flag的情況下,需要判斷SC flag
Secure connection的情況下,選用OOB。
Legacy的情況下,需要去判斷MITM flag,只要一方設定了MITM flag,就要通過I/O能力去選擇pairing mothed。如果兩方都沒有設定MITM flag,那么直接用Just works
3) Initiator和Responder都沒有設OOB的情況下,判斷MITM flag
只要一方設定了MITM,需要去判斷I/O能力
兩方都沒設定MITM的情況下,使用Just Works
(2) 密鑰生成階段
傳統配對方式,生成STK,流程如下:
傳統配對方式中,首先交換臨時密鑰 (TK),用于生成短期密鑰 (STK),然后使用短期密鑰 (STK) 加密鏈路,傳輸長期密鑰(LTK)。
1.1傳統配對定義了三種不同的方法來交互 TK,它們是Just works,Passkey Entry和Out of Band (OOB)。
Just Works:TK默認為0,不提供身份驗證,因此無法防御 MITM 攻擊。
Passkey Entry :一臺設備上顯示 6 位數字密碼(passkey),另一臺設備上需要輸入。設備的 I/O 能力決定了哪臺設備顯示數字以及哪臺設備輸入數字。用戶輸入的6位數字passkey,用前導零填充以得出 128 位TK值。
Out of Band:在設備之間通過帶外通信,傳遞完整 128 位TK值,例如使用 NFC傳輸。
注:Just Works 配對不提供身份驗證,無法防御 MITM 攻擊。
1.2 傳統配對的認證(authentication)過程:
首先,每個設備計算一個 128 位的確認值(confirm value)。確認值是使用名為 c1 的函數計算的,該函數接受多個參數,包括 TK。除了 TK 值之外,還包括兩個設備都知道的字段以及一個隨機數,在這個階段,只有創建確認值的設備知道這個隨機數。在主機上,這個值稱為LP_RAND_I。在從機上,稱為LP_RAND_R。
然后,主機將其確認值 (LP_CONFIRM_I) 發送到從機,從機以其確認值 (LP_CONFIRM_R) 進行響應。
收到 LP_CONFIRM_R 后,主機發送自己的隨機數 LP_RAND_I給從機。從機計算出LP_CONFIRM_I 值,并將其與主機端發送過來的 LP_CONFIRM_I 值進行比較。如果它們匹配,則從機已對主機進行了身份驗證,因此它將發送自己的隨機數LP_RAND_R進行響應。主機計算 LP_CONFIRM_R 值并將其與之前收到的值進行比較。如果它們匹配,則主機完成了對從機的身份驗證。
以Passkey為例,認證流程如下:
1.3 傳統配對中,STK的生成公式如下:
STK=s1(TK,LP_RAND_R,LP_RAND_I)
注意:TK值是不會被藍牙鏈路傳輸的,這確保了一定的安全性。TK的密碼強度直接影響了STK的密碼強度,顯然OOB要好一些。
安全配對方式, 生成LTK,流程如下:
LE 安全連接使用橢圓曲線 Diffie-Hellman (ECDH) 密鑰交換算法交換數據,然后使用數據創建對稱密鑰,稱為長期密鑰 (LTK)。
2.1 Public Key Exchange
發起配對的設備(發起者)將其公鑰發送給另一臺設備(響應者),后者用其公鑰進行回復。公鑰在收到后進行檢驗,以檢查它們是否在正確的橢圓曲線上。注意,LE 安全連接僅使用 P-256 曲線。
2.2 Calculate DHKey
每個設備都會計算一個稱為 Diffie-Hellman key (DHKey) 的密鑰。假設一方是Alice,一方是Bob,Alice 和 Bob 這兩個設備按以下方式計算 DHKey:
Alice: DHKey = P256(SKa, PKb) Bob: DHKey = P256(SKb, PKa)
兩個設備 A 和 B 的私鑰分別表示為 SKa 和 SKb。PKa和PKb是它們的公鑰,交換之后,雙方都知道對方的公鑰。基于公鑰和私鑰作為參數,兩邊能計算出相同的DHkey。DHKey 是一個對稱共享密鑰。
.3 Authentication Stage 1
不同的配對模式,對應的算法略有不同。LE安全配對有4種模式,除了前面提到的傳統配對中的3種模式以外,還引入了一種新模式,稱為數字比較(Numeric Comparison)
1) Just Works 和Numeric Comparison身份驗證階段1:
首先,設備 B 生成一個偽隨機數 Nb,并將其與兩個設備的公鑰一起,作為 f4 (函數)的參數,來計算確認值Cb 。Cb=f4(PKb, PKa, Nb, 0),然后將 Cb 發送到設備 A。由于設備 A 不知道 Nb 的值,因此它無法在此階段對確認值執行任何操作。
設備 A 現在將其自己的偽隨機數 Na 發送到設備 B,設備 B 則用其隨機數 Nb 回復。現在,設備 A 擁有 f4 函數的所有參數,因此它使用在公鑰交換期間獲取的公鑰和剛剛收到的另一臺設備的隨機數 Nb 重新計算確認值。它將計算出的確認值與從設備 B 收到的 Cb 值進行比較。如果它們不相同,則配對中止。
如果使用 Just Works,則身份驗證第 1 階段已完成。如果使用Numeric Comparison,則還需要執行一步。
每個設備使用兩個公鑰 PKa 和 PKb 以及兩個偽隨機數 Na 和 Nb,通過g2 函數計算出一個六位數字。然后,每個設備顯示其計算出的數字。用戶被邀請確認兩個設備是否顯示相同的六位數字,也可以通過按下特定按鈕來實現。通過指示兩個數字相同,用戶確認參與通信的兩個設備確實是用戶嘗試配對的設備。換句話說,用戶參與了配對設備的身份驗證。對設備進行身份驗證后,就會進入身份驗證階段 2。
另一方面,如果用戶發現兩個數字不相同,則身份驗證階段 1 失敗,并且終止該過程。
具體流程如下圖所示:
2) Passkey Entry身份驗證階段1:
用戶輸入密碼(Passkey)后,設備會計算、交換和檢查確認值 Ca 和 Cb。但是,與 Just Works/Numeric Comparison 中的單次確認不同,使用密碼(Passkey)輸入,確認過程是迭代和增量的。
在每次迭代過程中,每個設備都會生成一個 128 位隨機數 Na 或 Nb,并且每次將密鑰值的一位納入確認值的計算中。確認值函數 f4 的輸入包括兩個公鑰、本次迭代的隨機數以及從本次迭代的密碼位值派生的值。
每次迭代都會交換和檢查確認值。Cai=f4(Pka, PKb, Nai, rbi),Cbi=f4(PKb, PKa, Nbi, rbi)(i=0 to 19)(ra=rb=passkey)rai和rbi是passkey二進制值對應的每一位。
六位密碼(passkey)對應 20 位二進制數字(999999=0xF423F),因此計算、交換和檢查完整確認值的過程需要迭代20次,每次迭代都會合并或披露密碼的新位。這種方法稱為“逐步披露”,其優點是 MITM 攻擊在實際應用中更加困難,大多數攻擊在攻擊者尚未看到完整和最終確認值的情況下就會提前失敗。缺點是計算過程比較復雜,數據交互的次數較多,耗時耗電。
具體流程如下圖所示:
3) OOB身份驗證階段1:
使用 OOB 方式,整個過程使用非藍牙的數據交換機制進行的。
使用帶內接收的公鑰和帶外接收的隨機數 ra 和 rb 重新計算接收到的確認值 Ca 和 Cb,這個是基于OOB雙向數據傳輸的情況。如果 OOB 通信只能在一個方向上進行(單方向傳輸數據),則接收 OOB 數據的設備的認證將基于該設備知道通過 OOB 發送的隨機數 r(ra或rb)。在這種情況下,r 必須是保密的:r 可以每次重新創建,或者必須限制對發送 r 的設備的訪問。如果設備未發送 r,則設備會將其假定為 0。計算出的確認值必須與接收到的 OOB 值匹配。如果兩者之一不匹配,配對將中止。
2.4 Authentication Stage 2
身份驗證階段 2 有四個步驟。
1)使用函數 f5 計算長期密鑰 (LTK) 和 MacKey ,MacKey || LTK = f5(DHKey, Na, Nb, BD_ADDR_C, BD_ADDR_P)
2)設備 A 計算稱為 Ea 的檢查值并將其發送到設備 B,在設備 B 中重新計算該值并將其與收到的值進行比較
3)設備 B 計算稱為 Eb 的檢查值并將其發送到設備 A,在設備 A 中重新計算該值并將其與收到的值進行比較
4)使用計算出的 LTK 在鏈路上啟動加密,為階段 3 做好準備
如果步驟 2 或 3 中的任何一個檢查失敗,配對將中止。具體流程如下圖所示:
(3) 密鑰分發
對于傳統配對和LE安全連接,它們分發的內容是有區別的。另外,傳統配對密鑰分發的鏈路是通過STK加密的;LE安全連接的密鑰分發是通過LTK加密的。傳統配對可能分發的內容如下:
LTK by the Peripheral
EDIV AND Rand by the Peripheral
IRK by the Peripheral
BD_ADDR by the Peripheral
CSRK by the Peripheral
LTK by the Central
EDIV and Rand by the Central
IRK by the Central
BD_ADDR by the Central
CSRK by the Central
LE安全連接可能分發的內容如下:
IRK by the Peripheral
BD_ADDR by the Peripheral
CSRK by the Peripheral
IRK by the Central
BD_ADDR by the Central
CSRK by the Central
七. Bluetooth LE綁定
創建綁定后,每個設備都會存儲已分發的密鑰信息,并在下一次連接中使用這些密鑰,這樣就可以跳過配對過程,從而節省能源并減少用戶操作設備時必須等待的時間。
LTK 存儲在安全數據庫中,并通過 EDIV 和 Rand 值進行標識。執行綁定后,每次后續重新連接都將使用已分發的 LTK 生成的密鑰來加密鏈接。
NCS中可以通過配置編譯選項使能綁定功能(CONFIG_BT_BONDABLE=y)。注意,綁定信息存儲在settings區,更具體的是bt settings區域,所以,使能綁定功能的同時也要使能bt settings區。具體信息如下:
如果要刪除綁定信息,可以調用int bt_unpair(uint8_t id, const bt_addr_le_t *addr)函數,函數說明和使用示例如下:
/** * @brief Clear pairing information. * * @param id Local identity (mostly just BT_ID_DEFAULT). * @param addr Remote address, NULL or BT_ADDR_LE_ANY to clear all remote devices. * * @return 0 on success or negative error value on failure. */ int bt_unpair(unit8_t id, const bt_addr_le_t *addr);
void button_changed(unit32_t button_state,unit32_t has_changed) { int err; unit32_t buttons = button_state & has_changed; if(buttons & KEY_BOND_REMOVE_MASK) { err = bt_unpair(BT_ID_DEFAULT,NULL); if (err) { printk("Bond remove failed err: %dn", err); } else { printk("All bond removedn"); } } }
static int remove_peers(unit8_t identity) { LOG_INF("Remove peer on identity %u", identity); int err = bt_unpair(get_bt_stack_peer_id(identity), BT_ADDR_LE_ANY); if (err) { LOG_ERR("Failed to remove"); } return err; }
八. NCS中如何開啟和使用SMP相關功能
(1) 開啟SMP功能
SMP協議對應的代碼主要位于zephyrsubsysbluetoothhostsmp.c
設置CONFIG_BT_SMP=y可以加入smp.c到當前工程。
config BT_SMP bool "Security Manager Protocol support" select BT_CRYPTO select BT_RPA select BT_ECC help This option enables support for the Security Manager Protocol (SMP), making it possible to pair devices over LE.
if(CONFIG_BT_CONN) zephyr_library_sources( conn.c 12cap.c att.c gatt.c ) if(CONFIG_BT_SMP) zephyr_library_sources( smp.c keys.c ) else() zephyr_library_sources( smp_null.c ) endif() endif()
NCS中SMP的主要例子包括:
nrfsamplesbluetoothcentral_hids
nrfsamplesbluetoothperipheral_uart
nrfsamplesbluetoothperipheral_mds
nrfsamplesbluetoothperipheral_nfc_pairing
nrfsamplesbluetoothcentral_nfc_pairing
zephyrsamplesbluetoothperipheral_sc_only
zephyrsubsysbluetoothshell
Bluetooth LE例子里,只要設置了CONFIG_BT_SMP=y,至少支持just works
(2) 如何設置SC(安全連接)標志
NCS中使能SMP的Nordic的例子里,默認都是SC。SC需要用到很多安全算法,Nordic的芯片都能支持。為了保證Bluetooth LE通信的安全性,我們建議用SC的方式。但如果客戶資源(RAM,flash)不夠用,且對Bluetooth LE安全要求不高的情況下,可以關閉SC,使用傳統配對。
SC設置相關代碼段:
int bt_smp_init(void) { static struct bt_pub_key_cb pub_key_cb = { .func = bt_smp_pkey_ready, }; sc_supported = le_sc_supported(); if (IS_ENABLED(CONFIG_BT_SMP_SC_PAIR_ONLY) && !sc_supported) { LOG_ERR("SC Pair Only Mode selected but LE SC not supported"); return -ENOENT; } if (IS_ENABLED(CONFIG_BT_SMP_USB_HCI_CTLR_WORKAROUND)) { LOG_WRN("BT_SMP_USB_HCI_CTLR_WORKAROUND is enabled, which " "exposes a security vulnerability!"); } LOG_DBG("LE SC %s", sc_supported ? "enabled" : "disabled"); if (!IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) { bt_pub_key_gen(&pub_key_cb); } return smp_self_test(); }
static bool le_sc_supported(void) { /* * If controller based ECC is to be used it must support * "LE Read Local P-256 Public Key" and "LE Generate DH Key" commands. * Otherwise LE SC are not supported. */ if (IS_ENABLED(CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY)) { return false; } return BT_CMD_TEST(bt_dev.supported_commands, 34, 1) && BT_CMD_TEST(bt_dev.supported_commands, 34, 2); }
static void read_supported_commands_complete(struct net_buf *buf) { struct bt_hci_rp_read_supported_commands *rp = (void *)buf->data; LOG_DBG("status 0x%02x", rp->status); memcpy(bt_dev.supported_commands, rp->commands, sizeof(bt_dev.supported_commands)); /* Report additional HCI commands used for ECDH as * supported if TinyCrypt ECC is used for emulation. */ if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) { bt_hci_ecc_supported_commands(bt_dev.supported_commands); } }
void bt_hci_ecc_supported_commands(uint8_t *supported_commands) { /* LE Read Local P-256 Public Key */ supported_commands[34] |= BIT(1); /* LE Generate DH Key v1 */ supported_commands[34] |= BIT(2); /* LE Generate DH Key v2 */ supported_commands[41] |= BIT(2); }
#if defined(CONFIG_BT_CTLR_ECDH) case BT_HCI_OP_LE_P256_PUBLIC_KEY: return hci_cmd_le_read_local_p256_public_key(); case BT_HCI_OP_LE_GENERATE_DHKEY: return hci_cmd_le_generate_dhkey((void *)cmd_params); case BT_HCI_OP_LE_GENERATE_DHKEY_V2: return hci_cmd_le_generate_dhkey_v2((void *)cmd_params); #endif
1) 使用OOB的情況下,通過設置CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY=y去指定非SC。
config BT_SMP_OOB_LEGACY_PAIR_ONLY bool "Force Out Of Band Legacy pairing" depends on !(BT_SMP_SC_PAIR_ONLY || BT_SMP_SC_ONLY) help This option disables Legacy and LE SC pairing and forces legacy OOB.
2) 不使用OOB的情況下,設置CONFIG_BT_CTLR_ECDH=n和CONFIG_BT_TINYCRYPT_ECC=n。關閉安全算法的情況下,系統會使用傳統配對。
config BT_CTLR_ECDH bool "Elliptic Curve Diffie-Hellman (ECDH)" depends on BT_CTLR_ECDH_SUPPORT default y help Enable support for Bluetooth v4.2 Elliptic Curve Diffie-Hellman feature in the controller.
config BT_TINYCRYPT_ECDH bool "Emulate ECDH in the Host using TinyCrypt library" select TINYCRYPT select TINYCRYPT_ECC_DH select BT_LONG_WQ depends on BT_ECC && (BT_HCI_RAW || BT_HCI_HOST) default y if BT_CTLR && !BT_CTLR_ECDH help If this option is set TinyCrypt library is used for emulating the ECDH HCI commands and events need by e.g LE Secure Connections. In builds including the BLE Host, if not set the controller crypto is used for ECDH and if the controller doesn't support the required HCI commands the LE Secure Connections support will be disable. In builds including the HCI RAW interface and the BLE Controller, this option injects support for the 2 HCI commands required for LE Secure Connections so that Hosts can make use of those. The option defaults to enable for a combined build with Zephyr's own controller, since it does not have any special ECC support itself (at least not currently).
另外,調用int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec)函數時,第二個參數如果是BT_SECURITY_L4,且I/O能力滿足條件,則會發起SC;否則,使用傳統配對。
/** @brief Set security level for a connection. * * This function enable security (encryption) for a connection. If the device * has bond information for the peer with sufficiently strong key encryption * will be enabled. If the connection is already encrypted with sufficiently * strong key this function does nothing. * * If the device has no bond information for the peer and is not already paired * then the pairing procedure will be initiated. Note that @p sec has no effect * on the security level selected for the pairing process. The selection is * instead controlled by the values of the registered @ref bt_conn_auth_cb. If * the device has bond information or is already paired and the keys are too * weak then the pairing procedure will be initiated. * * This function may return error if required level of security is not possible * to achieve due to local or remote device limitation (e.g., input output * capabilities), or if the maximum number of paired devices has been reached. * * This function may return error if the pairing procedure has already been * initiated by the local device or the peer device. * * @note When @kconfig{CONFIG_BT_SMP_SC_ONLY} is enabled then the security * level will always be level 4. * * @note When @kconfig{CONFIG_BT_SMP_OOB_LEGACY_PAIR_ONLY} is enabled then the * security level will always be level 3. * * @note When @ref BT_SECURITY_FORCE_PAIR within @p sec is enabled then the pairing * procedure will always be initiated. * * @param conn Connection object. * @param sec Requested security level. * * @return 0 on success or negative error */ int bt_conn_set_security(struct bt_conn *conn, bt_security_t sec);
注:是否使用SC,需要兩方協商,一方不能決定。
(3) 如何設置MITM標志
通過設定CONFIG_BT_SMP_ENFORCE_MITM=y編譯選項,可以強制設置MITM標志。
config BT_SMP_ENFORCE_MITM bool "Enforce MITM protection" default y help with this option enabled, the Security Manager will set MITM option in the Authentication Requirements Flags whenever local IO Capabilities allow the generated key to be authenticated.
static uint8_t get_auth(struct bt_smp *smp, uint8_t auth) { struct bt_conn *conn = smp->chan.chan.conn; if (sc_supported) { auth &= BT_SMP_AUTH_MASK_SC; } else { auth &= BT_SMP_AUTH_MASK; } if ((get_io_capa(smp) == BT_SMP_IO_NO_INPUT_OUTPUT) || (!IS_ENABLED(CONFIG_BT_SMP_ENFORCE_MITM) && (conn->required_sec_level < BT_SECURITY_L3))) { auth &= ~(BT_SMP_AUTH_MITM); } else { auth |= BT_SMP_AUTH_MITM; } if (latch_bondable(smp)) { auth |= BT_SMP_AUTH_BONDING; } else { auth &= ~BT_SMP_AUTH_BONDING; } if (IS_ENABLED(CONFIG_BT_PASSKEY_KEYPRESS)) { auth |= BT_SMP_AUTH_KEYPRESS; } else { auth &= ~BT_SMP_AUTH_KEYPRESS; } return auth; }
當CONFIG_BT_SMP_ENFORCE_MITM=n時,SC flag和I/O能力共同決定了MITM flag。
下圖中Authenticated對應的MITM=1,Unauthenticated對應的MITM=0。
(4) 如何設置OOB數據 flag
準備好OOB數據后,通過調用API函數設置此flag。SC和非SC的設置函數分別如下:
/** @brief Allow/disallow remote LE SC OOB data to be used for pairing. * * Set/clear the OOB data flag for LE SC SMP Pairing Request/Response data. * * @param enable Value allowing/disallowing remote LE SC OOB data. */ void bt_le_oob_set_sc_flag(bool enable);
/** @brief Allow/disallow remote legacy OOB data to be used for pairing. * * Set/clear the OOB data flag for legacy SMP Pairing Request/Response data. * * @param enable Value allowing/disallowing remote legacy OOB data. */ void bt_le_oob_set_legacy_flag(bool enable); /** @brief Set OOB Temporary Key to be used for pairing * * This function allows to set OOB data for the LE legacy pairing procedure. * The function should only be called in response to the oob_data_request() * callback provided that the legacy method is user pairing. * * @param conn Connection object * @param tk Pointer to 16 byte long TK array * * @return Zero on success or -EINVAL if NULL */
OOB數據 flag設置相關的代碼片段:
void bt_le_oob_set_sc_flag(bool enable) { sc_oobd_present = enable; } void bt_le_oob_set_legacy_flag(bool enable) { legacy_oobd_present = enable; }
/* At this point is it unknown if pairing will be legacy or LE SC so * set OOB flag if any OOB data is present and assume to peer device * provides OOB data that will match it's pairing type. */ req->oob_flag = (legacy_oobd_present || sc_oobd_present) ? BT_SMP_OOB_PRESENT : BT_SMP_OOB_NOT_PRESENT;
if (atomic_test_bit(smp->flags, SMP_FLAG_SC)) { rsp->oob_flag = sc_oobd_present ? BT_SMP_OOB_PRESENT : BT_SMP_OOB_NOT_PRESENT; } else { rsp->oob_flag = legacy_oobd_present ? BT_SMP_OOB_PRESENT : BT_SMP_OOB_NOT_PRESENT; }
從配對的過程來看,OOB的數據在兩個階段中使用,一個階段是帶外數據交互,另一個階段是身份驗證的過程中充當參數。
非SC的OOB帶外需要交互TK值。TK值實際上也是一個Random Number。
SC 的OOB帶外需要交互Random Number和Confirm Value。
另外還有一些附加的輔助信息。我們peripheral_nfc_pairing和central_nfc_pairing例子里面,NFC的信息如下(兼容支持SC和非SC):
/** * @brief LE OOB record payload descriptor. */ struct nfc_ndef_le_oob_rec_payload_desc { bt_addr_le_t *addr; enum nfc_ndef_le_oob_rec_le_role *le_role; struct bt_le_oob_sc_data *le_sc_data; uint8_t *tk_value; uint16_t *appearance; uint8_t *flags; const char *local_name; };
/** .. include_startingpoint_nfc_tnep_ch_tag_rst */ static int carrier_prepare(void) { static struct nfc_ndef_le_oob_rec_payload_desc rec_payload; NFC_NDEF_LE_OOB_RECORD_DESC_DEF(oob_rec, '0', &rec_payload); NFC_NDEF_CH_AC_RECORD_DESC_DEF(oob_ac, NFC_AC_CPS_ACTIVE, 1, "0", 0); memset(&rec_payload, 0, sizeof(rec_payload)); rec_payload.addr = &oob_local.addr; rec_payload.le_sc_data = &oob_local.le_sc_data; rec_payload.tk_value = tk_value; rec_payload.local_name = bt_get_name(); rec_payload.le_role = NFC_NDEF_LE_OOB_REC_LE_ROLE( NFC_NDEF_LE_OOB_REC_LE_ROLE_PERIPH_ONLY); rec_payload.appearance = NFC_NDEF_LE_OOB_REC_APPEARANCE( CONFIG_BT_DEVICE_APPEARANCE); rec_payload.flags = NFC_NDEF_LE_OOB_REC_FLAGS(BT_LE_AD_NO_BREDR); return nfc_tnep_ch_carrier_set(&NFC_NDEF_CH_AC_RECORD_DESC(oob_ac), &NFC_NDEF_LE_OOB_RECORD_DESC(oob_rec), 1); }
/** LE Secure Connections pairing Out of Band data. */ struct bt_le_oob_sc_data { /** Random Number. */ uint8_t r[16]; /** Confirm Value. */ uint8_t c[16]; }; /** LE Out of Band information. */ struct bt_le_oob { /** LE address. If privacy is enabled this is a Resolvable Private * Address. */ bt_addr_le_t addr; /** LE Secure Connections pairing Out of Band data. */ struct bt_le_oob_sc_data le_sc_data; };
通過int bt_le_oob_get_local(uint8_t id, struct bt_le_oob *oob)或int bt_le_ext_adv_oob_get_local(struct bt_le_ext_adv *adv, struct bt_le_oob *oob)可以從controller獲取本地生成的SC OOB數據。這個數據需要寫入帶外設備,如NFC 設備。
/** * @brief Get local LE Out of Band (OOB) information. * * This function allows to get local information that are useful for * Out of Band pairing or connection creation. * * If privacy @kconfig{CONFIG_BT_PRIVACY} is enabled this will result in * generating new Resolvable Private Address (RPA) that is valid for * @kconfig{CONFIG_BT_RPA_TIMEOUT} seconds. This address will be used for * advertising started by @ref bt_le_adv_start, active scanning and * connection creation. * * @note If privacy is enabled the RPA cannot be refreshed in the following * cases: * - Creating a connection in progress, wait for the connected callback. * In addition when extended advertising @kconfig{CONFIG_BT_EXT_ADV} is * not enabled or not supported by the controller: * - Advertiser is enabled using a Random Static Identity Address for a * different local identity. * - The local identity conflicts with the local identity used by other * roles. * * @param[in] id Local identity, in most cases BT_ID_DEFAULT. * @param[out] oob LE OOB information * * @return Zero on success or error code otherwise, positive in case of * protocol error or negative (POSIX) in case of stack internal error. */ int bt_le_oob_get_local(uint8_t id, struct bt_le_oob *oob);
/** * @brief Get local LE Out of Band (OOB) information. * * This function allows to get local information that are useful for * Out of Band pairing or connection creation. * * If privacy @kconfig{CONFIG_BT_PRIVACY} is enabled this will result in * generating new Resolvable Private Address (RPA) that is valid for * @kconfig{CONFIG_BT_RPA_TIMEOUT} seconds. This address will be used by the * advertising set. * * @note When generating OOB information for multiple advertising set all * OOB information needs to be generated at the same time. * * @note If privacy is enabled the RPA cannot be refreshed in the following * cases: * - Creating a connection in progress, wait for the connected callback. * * @param[in] adv The advertising set object * @param[out] oob LE OOB information * * @return Zero on success or error code otherwise, positive in case * of protocol error or negative (POSIX) in case of stack internal error. */ int bt_le_ext_adv_oob_get_local(struct bt_le_ext_adv *adv, struct bt_le_oob *oob);
通過調用int bt_le_oob_set_sc_data(struct bt_conn *conn, const struct bt_le_oob_sc_data *oobd_local, const struct bt_le_oob_sc_data *oobd_remote)可以設置SC OOB配對過程中,身份認證階段所需要的數據。
/** @brief Set OOB data during LE Secure Connections (SC) pairing procedure * * This function allows to set OOB data during the LE SC pairing procedure. * The function should only be called in response to the oob_data_request() * callback provided that LE SC method is used for pairing. * * The user should submit OOB data according to the information received in the * callback. This may yield three different configurations: with only local OOB * data present, with only remote OOB data present or with both local and * remote OOB data present. * * @param conn Connection object * @param oobd_local Local OOB data or NULL if not present * @param oobd_remote Remote OOB data or NULL if not present * * @return Zero on success or error code otherwise, positive in case of * protocol error or negative (POSIX) in case of stack internal error. */ int bt_le_oob_set_sc_data(struct bt_conn *conn, const struct bt_le_oob_sc_data *oobd_local, const struct bt_le_oob_sc_data *oobd_remote);
非SC的OOB,通過int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk)設置身份認證階段所需要的tk。
/** @brief Set OOB Temporary Key to be used for pairing * * This function allows to set OOB data for the LE legacy pairing procedure. * The function should only be called in response to the oob_data_request() * callback provided that the legacy method is user pairing. * * @param conn Connection object * @param tk Pointer to 16 byte long TK array * * @return Zero on success or -EINVAL if NULL */ int bt_le_oob_set_legacy_tk(struct bt_conn *conn, const uint8_t *tk);
(5) 如何設置IO能力
IO能力有如下5種:
#define BT_SMP_IO_DISPLAY_ONLY 0x00 #define BT_SMP_IO_DISPLAY_YESNO 0x01 #define BT_SMP_IO_KEYBOARD_ONLY 0x02 #define BT_SMP_IO_NO_INPUT_OUTPUT 0x03 #define BT_SMP_IO_KEYBOARD_DISPLAY 0x04
首先看看獲取IO能力函數uint8_t get_io_capa(struct bt_smp *smp)的實現,如下圖:
static uint8_t get_io_capa(struct bt_smp *smp) { const struct bt_conn_auth_cb *smp_auth_cb = latch_auth_cb(smp); if (!smp_auth_cb) { goto no_callbacks; } /* Passkey Confirmation is valid only for LE SC */ if (smp_auth_cb->passkey_display && smp_auth_cb->passkey_entry && (smp_auth_cb->passkey_confirm || !sc_supported)) { return BT_SMP_IO_KEYBOARD_DISPLAY; } /* DisplayYesNo is useful only for LE SC */ if (sc_supported && smp_auth_cb->passkey_display && smp_auth_cb->passkey_confirm) { return BT_SMP_IO_DISPLAY_YESNO; } if (smp_auth_cb->passkey_entry) { if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_INVALID) { return BT_SMP_IO_KEYBOARD_DISPLAY; } else { return BT_SMP_IO_KEYBOARD_ONLY; } } if (smp_auth_cb->passkey_display) { return BT_SMP_IO_DISPLAY_ONLY; } no_callbacks: if (IS_ENABLED(CONFIG_BT_FIXED_PASSKEY) && fixed_passkey != BT_PASSKEY_INVALID) { return BT_SMP_IO_DISPLAY_ONLY; } else { return BT_SMP_IO_NO_INPUT_OUTPUT; } }
上面的代碼能看到一個關鍵的結構體struct bt_conn_auth_cb,結構體的定義如下圖:
結構體對應變量的成員都是callback函數。部分callback函數定義與否,決定了本設備的IO能力。
1)no callbacks的情況下,如果沒有設置CONFIG_BT_FIXED_PASSKEY,那么IO能力就是BT_SMP_IO_NO_INPUT_OUTPUT;如果設置CONFIG_BT_FIXED_PASSKEY=y且fixed_passkey != BT_PASSKEY_INVALID,那么IO能力就是BT_SMP_IO_DISPLAY_ONLY
config BT_FOEXED_PASSKEY bool "Use a fixed passkey for pairing" help With this option enabled, the application will be able to call the bt_passley_set() API to set a fixed passkey. If set, the pairing_confirm() callback will be called for all incoming pairings.
注:如果使用fixed passkey,應用需要調用bt_passkey_set()函數去設置passkey。沒有IO能力的設備,通過使用fixed passkey,可以偽裝成有display能力的設備走passkey entry的配對流程。本設備和對方設備需要共同知道這個passkey,因為它沒有真正被顯示出來(對方也需要有輸入passkey的能力),且passkey不能暴露給不可信賴的第三方。
2)非no callbacks的情況下,如果定義了passkey_display,passkey_entry和passkey_confirm三個 callbacks,那么IO能力是BT_SMP_IO_KEYBOARD_DISPLAY;如果定義了passkey_display和passkey_entry兩個callbacks且非SC, 那么IO能力也是BT_SMP_IO_KEYBOARD_DISPLAY
3)非no callbacks的情況下,如果定義了passkey_display和passkey_confirm兩個callbacks且SC, 那么IO能力是BT_SMP_IO_DISPLAY_YESNO
4)非no callbacks的情況下,如果只定義了passkey_entry一個callback,當CONFIG_BT_FIXED_PASSKEY=y且fixed_passkey != BT_PASSKEY_INVALID時,IO能力是BT_SMP_IO_KEYBOARD_DISPLAY,否則,IO能力是BT_SMP_IO_ KEYBOARD _ONLY
注:如果使用fixed passkey,那么相當與只有KEYBOARD輸入的設備,偽裝成了有KEYBOARD輸入和DISPLAY輸出能力的設備。
5)非no callbacks的情況下,如果只定義了passkey_display一個callback,IO能力是BT_SMP_IO_DISPLAY_ONLY
注意,最終的配對方式和行為是本設備的綜合能力和對方設備的綜合能力共同決定的。用戶在設計自己應用的時候需要根據自己設備實際的IO能力和設備角色(central/peripheral)去做配置和編寫相應的代碼,同時也要考慮安全性和兼容性。
如何配置IO能力
定義和實現好相關的callback函數就可以控制IO能力了。IO能力越強的設備,如果要做好兼容性,代碼的復雜性會更高一些。拿從機來舉例,如果沒有輸入輸出能力,那么只能支持Jusk works(fixed passkey除外),也只需要支持Jusk works;如果只有display的能力,如下圖,它需要兼容Jusk works和 passkey entry(passkey display)
如果有display和Keyboard的能力,如下圖,它需要兼容Jusk works, passkey entry(passkey input,passkey display)和Numeric Comparison
(6)結構體struct bt_conn_auth_cb callback主要函數的作用
passkey_display callback的作用是從底層向應用層提供passkey。Passkey entry配對過程中,這個passkey需要通過本機的顯示接口展示給用戶。用戶再將這個passkey通過對端設備的輸入接口輸入給對端。正常情況下,用戶只會給他信任的設備輸入passkey,passkey起到了身份驗證的作用。nrfsamplesbluetoothperipheral_uart例子演示了passkey_display callback的使用方法。
/** @brief Display a passkey to the user. * * When called the application is expected to display the given * passkey to the user, with the expectation that the passkey will * then be entered on the peer device. The passkey will be in the * range of 0 - 999999, and is expected to be padded with zeroes so * that six digits are always shown. E.g. the value 37 should be * shown as 000037. * * This callback may be set to NULL, which means that the local * device lacks the ability do display a passkey. If set * to non-NULL the cancel callback must also be provided, since * this is the only way the application can find out that it should * stop displaying the passkey. * * @param conn Connection where pairing is currently active. * @param passkey Passkey to show to the user. */ void (*passkey_display)(struct bt_conn *conn, unsigned int passkey); #if defined(CONFIG_BT_PASSKEY_KEYPRESS) /** @brief Receive Passkey Keypress Notification during pairing * * This allows the remote device to use the local device to give users * feedback on the progress of entering the passkey over there. This is * useful when the remote device itself has no display suitable for * showing the progress. * * The handler of this callback is expected to keep track of the number * of digits entered and show a password-field-like feedback to the * user. * * This callback is only relevant while the local side does Passkey * Display. * * The event type is verified to be in range of the enum. No other * sanitization has been done. The remote could send a large number of * events of any type in any order. * * @param conn The related connection. * @param type Type of event. Verified in range of the enum. */ void (*passkey_display_keypress)(struct bt_conn *conn, enum bt_conn_auth_keypress type); #endif
passkey_entry callback的作用是提示用戶輸入passkey。Passkey entry配對過程中,等待用戶輸入passkey的時候,這個callback函數被調用。當passkey被用戶輸入后,應用需要調用bt_conn_auth_passkey_entry()API,將passkey從應用層傳到底層。nrfapplicationsnrf_desktopsrcmodulesble_passkey.c例子演示了passkey_entry callback的使用方法。
/** @brief Request the user to enter a passkey. * * When called the user is expected to enter a passkey. The passkey * must be in the range of 0 - 999999, and should be expected to * be zero-padded, as that's how the peer device will typically be * showing it (e.g. 37 would be shown as 000037). * * Once the user has entered the passkey its value should be given * to the stack using the bt_conn_auth_passkey_entry() API. * * This callback may be set to NULL, which means that the local * device lacks the ability to enter a passkey. If set to non-NULL * the cancel callback must also be provided, since this is the * only way the application can find out that it should stop * requesting the user to enter a passkey. * * @param conn Connection where pairing is currently active. */ void (*passkey_entry)(struct bt_conn *conn);
passkey_confirm callback的作用是從底層向應用層提供 passkey(user confirm value)并提示用戶是否確認。Numeric Comparison方式的配對過程中,兩邊的設備都會計算出一個數字(user confirm value),需要給用戶確認,確認它們是否相同。如果兩邊的數字相同,說明這兩個設備是可以被用戶信任的。用戶確認后,配對才能繼續。passkey_display提供的passkey實際上是user confirm value。另外,如果用戶確認兩邊的數字相同,應用需要調用bt_conn_auth_passkey_confirm()API去通知底層確認值匹配;如果不同,應用需要調用bt_conn_auth_cancel()API去通知底層確認值不匹配。nrfsamplesbluetoothperipheral_uart例子演示了passkey_confirm callback的使用方法。
/** @brief Request the user to confirm a passkey. * * When called the user is expected to confirm that the given * passkey is also shown on the peer device.. The passkey will * be in the range of 0 - 999999, and should be zero-padded to * always be six digits (e.g. 37 would be shown as 000037). * * Once the user has confirmed the passkey to match, the * bt_conn_auth_passkey_confirm() API should be called. If the * user concluded that the passkey doesn't match the * bt_conn_auth_cancel() API should be called. * * This callback may be set to NULL, which means that the local * device lacks the ability to confirm a passkey. If set to non-NULL * the cancel callback must also be provided, since this is the * only way the application can find out that it should stop * requesting the user to confirm a passkey. * * @param conn Connection where pairing is currently active. * @param passkey Passkey to be confirmed. */ void (*passkey_confirm)(struct bt_conn *conn, unsigned int passkey);
pairing_confirm callback和IO能力沒有關系,它用來通知應用,收到了pairing request請求。這里留出的接口,可以讓用戶做出是否同意配對的選擇。如果同意進行配對,那么應用需要調用bt_conn_auth_pairing_confirm()API表示同意;如果用戶不同意進行配對,應用可以調用bt_conn_auth_cancel()取消配對。當使用fixed key或just works配對方式時,可以定義這個callback函數,提示用戶是否同意配對,添加用戶的控制權。nrfsamplesbluetoothperipheral_mds例子演示了pairing_confirm callback的使用方法。
/** @brief Request confirmation for an incoming pairing. * * This callback will be called to confirm an incoming pairing * request where none of the other user callbacks is applicable. * * If the user decides to accept the pairing the * bt_conn_auth_pairing_confirm() API should be called. If the * user decides to reject the pairing the bt_conn_auth_cancel() API * should be called. * * This callback may be set to NULL, which means that the local * device lacks the ability to confirm a pairing request. If set * to non-NULL the cancel callback must also be provided, since * this is the only way the application can find out that it should * stop requesting the user to confirm a pairing request. * * @param conn Connection where pairing is currently active. */ void (*pairing_confirm)(struct bt_conn *conn);
oob_data_request callback和IO能力也沒有關系,它用于OOB配對,通知應用,需要提供OOB數據了。當應用層準備好這些數據后,需要調用bt_le_oob_set_sc_data() API(SC OOB)或bt_le_oob_set_legacy_tk() API(Legacy OOB)將數據傳給底層。nrfsamplesbluetoothcentral_nfc_pairing例子演示了oob_data_request callback的使用方法。
/** @brief Request the user to provide Out of Band (OOB) data. * * When called the user is expected to provide OOB data. The required * data are indicated by the information structure. * * For LE Secure Connections OOB pairing, the user should provide * local OOB data, remote OOB data or both depending on their * availability. Their value should be given to the stack using the * bt_le_oob_set_sc_data() API. * * This callback must be set to non-NULL in order to support OOB * pairing. * * @param conn Connection where pairing is currently active. * @param info OOB pairing information. */ void (*oob_data_request)(struct bt_conn *conn, struct bt_conn_oob_info *info);
pairing_accept callback只有在設置了CONFIG_BT_SMP_APP_PAIRING_ACCEPT=y的情況下,才能使用。pairing_accept和pairing_confirm的功能上有點重合。pairing_confirm收到pairing req會被調用;pairing_accept收到pairing req或rsp都會被調用。用戶通過它們可以去干預與和控制配對過程。nrfapplicationsnrf_desktopsrcmodulesfast_pair.c例子演示了pairing_accept callback的使用方法。
* On any incoming pairing req/rsp this callback will be called for * the application to decide whether to allow for the pairing to * continue. * * The pairing info received from the peer is passed to assist * making the decision. * * As this callback is synchronous the application should return * a response value immediately. Otherwise it may affect the * timing during pairing. Hence, this information should not be * conveyed to the user to take action. * * The remaining callbacks are not affected by this, but do notice * that other callbacks can be called during the pairing. Eg. if * pairing_confirm is registered both will be called for Just-Works * pairings. * * This callback may be unregistered in which case pairing continues * as if the Kconfig flag was not set. * * This callback is not called for BR/EDR Secure Simple Pairing (SSP). * * @param conn Connection where pairing is initiated. * @param feat Pairing req/resp info. */ enum bt_security_err (*pairing_accept)(struct bt_conn *conn, const struct bt_conn_pairing_feat *const feat);
cancel callback通知應用,之前的用戶請求(密碼顯示、輸入 或確認)被取消了。應用通過cancel callback函數可以提示用戶,配對被取消了。只要定義了
passkey_display、passkey_entry、 passkey_confirm 或 pairing_confirm,那么就必須定義cancel callback。
/** @brief Cancel the ongoing user request. * * This callback will be called to notify the application that it * should cancel any previous user request (passkey display, entry * or confirmation). * * This may be set to NULL, but must always be provided whenever the * passkey_display, passkey_entry passkey_confirm or pairing_confirm * callback has been provided. * * @param conn Connection where pairing is currently active. */ void (*cancel)(struct bt_conn *conn);
應用使用bt_conn_auth_cb_register()函數注冊這些callback。例子片段如下:
//display only static struct bt_conn_auth_cb conn_auth_callbacks = { .passkey_display = auth_passkey_display, .cancel = auth_cancel, };
//Keyboard only static struct bt_conn_auth_cb conn_auth_callbacks = { .passkey_entry = auth_passkey_entry, .cancel = auth_cancel, };
//display yes no static struct bt_conn_auth_cb conn_auth_callbacks = { .passkey_display = auth_passkey_display, .passkey_confirm = auth_passkey_confirm, .cancel = auth_cancel, };
//Keyboard display static struct bt_conn_auth_cb conn_auth_callbacks = { .passkey_display = auth_passkey_display, .passkey_entry = auth_passkey_entry, .passkey_confirm = auth_passkey_confirm, .cancel = auth_cancel, };
err = bt_conn_auth_cb_register(&conn_auth_callbacks);
(7) 結構體struct bt_conn_auth_info_cb用于通知應用,配對結果(成功或失?。┮约敖壎ㄐ畔h除結果。如果應用對這些信息感興趣可以定義相應的callback函數。
/** Authenticated pairing information callback structure */ struct bt_conn_auth_info_cb { /** @brief notify that pairing procedure was complete. * * This callback notifies the application that the pairing procedure * has been completed. * * @param conn Connection object. * @param bonded Bond information has been distributed during the * pairing procedure. */ void (*pairing_complete)(struct bt_conn *conn, bool bonded); /** @brief notify that pairing process has failed. * * @param conn Connection object. * @param reason Pairing failed reason */ void (*pairing_failed)(struct bt_conn *conn, enum bt_security_err reason); /** @brief Notify that bond has been deleted. * * This callback notifies the application that the bond information * for the remote peer has been deleted * * @param id Which local identity had the bond. * @param peer Remote address. */ void (*bond_deleted)(uint8_t id, const bt_addr_le_t *peer); /** Internally used field for list handling */ sys_snode_t node; };
應用使用bt_conn_auth_info_cb_register()函數注冊這些callback。例子片段如下:
static struct bt_conn_auth_info_cb conn_auth_info_callbacks = { .pairing_complete = pairing_complete, .pairing_failed = pairing_failed };
err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
注:本文中的圖表主要摘自Bluetooth Core Specification,Bluetooth LE Security Study Guide以及Nordic官網資料;代碼摘自nRF Connect SDK 2.6.0。
審核編輯 黃宇
-
NRF
+關注
關注
0文章
49瀏覽量
38202 -
SDK
+關注
關注
3文章
1076瀏覽量
48037 -
Bluetooth LE
+關注
關注
0文章
323瀏覽量
2490
發布評論請先 登錄
泰凌微電子Bluetooth LE Audio Dongle方案介紹

Nordic 推出nRF Connect for Cloud 的無線物聯網設計方案
nRF Connect SDK(NCS)/Zephyr固件升級詳解 – 重點講述MCUboot和藍牙空中升級

2.4GHz+5GHz雙頻低功耗 Wi-Fi 6協同IC -nRF7002
Nordic藍牙開發不用Keil用VS code了?
Nordic nRF Connect SDK 官方開發文檔、學習資料下載鏈接
Bluetooth LE Link Layer數據包全解析
Bluetooth LE Packet格式
NRF24LE1/NRF24LE1D-F16Q24/NRF24LE1E-F16Q32/NRF24LE1F-F16Q48長期現貨穩定配合
nRF24LE1減少功耗的編程技巧

nRF24LE1功耗測試與計算
nRF24LE1的喚醒源

評論