OpenHarmony藍(lán)牙自動(dòng)配對(duì)流程分析
想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):
前言
大家在實(shí)際使用藍(lán)牙時(shí)會(huì)發(fā)現(xiàn),有些藍(lán)牙設(shè)備配對(duì)需要輸入配對(duì)碼,有些藍(lán)牙設(shè)備則會(huì)自動(dòng)配對(duì);那這些設(shè)備有什么區(qū)別,OpenHarmony的藍(lán)牙協(xié)議棧又是怎么實(shí)現(xiàn)的呢?本文對(duì)此進(jìn)行分析和解讀。
藍(lán)牙協(xié)議分析
SSP(SECURE SIMPLE PAIRING)時(shí)當(dāng)前藍(lán)牙協(xié)議中最推薦采用的認(rèn)證配對(duì)方案;在SSP配對(duì)模式下,認(rèn)證配對(duì)總體分為兩步:IO Capability信息交換和用戶確認(rèn)。
IO Capability
藍(lán)牙設(shè)備按照輸入輸出能力分為四類,以設(shè)備A作Initiator組合后認(rèn)證配置方式如下表:
設(shè)備B\設(shè)備A | DisplayOnly | DisplayYesNo | KeyboardOnly | NoInputNoOutput |
DisplayOnly | 自動(dòng)配對(duì) | A用戶確認(rèn),B自動(dòng)配對(duì) | B顯示數(shù)字,A輸入 | 自動(dòng)配對(duì) |
DisplayYesNo | A自動(dòng)配對(duì),B用戶確認(rèn) | 用戶確認(rèn) | B顯示數(shù)字,A輸入 | A自動(dòng)配對(duì),B用戶確認(rèn) |
KeyboardOnly | A顯示數(shù)字,B輸入 | A顯示數(shù)字,B輸入 | 輸入passkey | 自動(dòng)配對(duì) |
NoInputNoOutput | 自動(dòng)配對(duì) | B自動(dòng)配對(duì),A用戶確認(rèn) | 自動(dòng)配對(duì) | 自動(dòng)配對(duì) |
交換設(shè)備IO Capability信息流程如下圖:
MITM Protection
參考藍(lán)牙core specification Version 5.4 | Vol 4, Part E, 7.1.29,可以發(fā)現(xiàn)IO Capability消息中除了IO_Capability字段還包括Authentication_Requirements字段,該字段同樣影響設(shè)備配對(duì)流程。
man-in-the-middle(MITM) ,中間人攻擊是一種常見(jiàn)的攻擊手法,藍(lán)牙SSP機(jī)制在用戶確認(rèn)模式時(shí)可以有效防止中間人攻擊。
協(xié)議規(guī)定:如果兩臺(tái)設(shè)備都明確指定不需要進(jìn)行MITM攻擊保護(hù),設(shè)備應(yīng)該按照自動(dòng)匹配流程處理。
用戶確認(rèn)
host收到User_Confirmation_Request消息后需要按照上表中的IO Capability要求用戶確認(rèn)或自動(dòng)回復(fù)確認(rèn)信息。
OpenHarmony實(shí)現(xiàn)流程
IO Capability信息交換
void GapOnIOCapabilityResponseEvent(const HciIoCapabilityResponseEventParam *eventParam)
{
LOG_DEBUG("%{public}s:" BT_ADDR_FMT "", __FUNCTION__, BT_ADDR_FMT_OUTPUT(eventParam->bdAddr.raw));
BtAddr addr = BT_ADDR_NULL;
GapChangeHCIAddr(&addr, &eventParam->bdAddr, BT_PUBLIC_DEVICE_ADDRESS);
DeviceInfo *devInfo = NULL;
devInfo = ListForEachData(GapGetConnectionInfoBlock()->devicelist, GapFindConnectionDeviceByAddr, (void *)&addr);
if (devInfo != NULL) {
devInfo->remoteAuthReq = eventParam->authenticationRequirements;
}
if (g_authenticationCallback.callback.IOCapabilityRsp) {
g_authenticationCallback.callback.IOCapabilityRsp(
&addr, eventParam->IOCapability, g_authenticationCallback.context);
}
}
GapOnIOCapabilityResponseEvent函數(shù)處理對(duì)端設(shè)備的IOCapability信息,remoteAuthReq保存對(duì)端設(shè)備的認(rèn)證要求;同時(shí)在ClassicAdapter模塊保存對(duì)端設(shè)備IOCapability能力;這里比較奇怪的是IOCapability和remoteAuthReq分在兩個(gè)模塊保存。
void ClassicAdapter::SaveRemoteIoCapability(const BtAddr &addr, uint8_t ioCapability)
{
HILOGI("enter");
RawAddress device = RawAddress::ConvertToString(addr.addr);
std::shared_ptr<ClassicRemoteDevice> remoteDevice = FindRemoteDevice(device);
remoteDevice->SetIoCapability(ioCapability);
}
確認(rèn)處理
void GapOnUserConfirmationRequestEvent(const HciUserConfirmationRequestEventParam *eventParam)
{
/* ... */
int localMitmRequired = GAP_MITM_REQUIRED;
int remoteMitmRequired = GAP_MITM_REQUIRED;
DeviceInfo *devInfo =
ListForEachData(GapGetConnectionInfoBlock()->devicelist, GapFindConnectionDeviceByAddr, (void*)&addr);
if (devInfo != NULL) {
remoteMitmRequired = devInfo->remoteAuthReq & GAP_MITM_REQUIRED;
if (devInfo->actionReq != NULL) {
if (!devInfo->actionReq->needAuthentication && devInfo->actionReq->needUnauthentication) {
localMitmRequired = GAP_MITM_NOT_REQUIRED;
}
} else {
localMitmRequired = remoteMitmRequired;
}
}
if (g_authenticationCallback.callback.userConfirmReq) {
g_authenticationCallback.callback.userConfirmReq(
&addr, eventParam->numericValue,localMitmRequired, remoteMitmRequired, g_authenticationCallback.context);
} else {
GapUserConfirmationRequestNegativeReply(&addr);
}
}
GapOnUserConfirmationRequestEvent函數(shù)獲取到IO Capability交換流程中保存認(rèn)證要求,并獲取本設(shè)備最近一次連接的認(rèn)證設(shè)置,作為參數(shù)傳遞到ClassicAdapter::SSPConfirmReq函數(shù)進(jìn)行處理。
void ClassicAdapter::SSPConfirmReq(const BtAddr &addr, int reqType, int number,
int localMitmRequired, int remoteMitmRequired)
{
HILOGI("reqTyep: %{public}d", reqType);
RawAddress device = RawAddress::ConvertToString(addr.addr);
std::shared_ptr<ClassicRemoteDevice> remoteDevice = FindRemoteDevice(device);
remoteDevice->SetPairConfirmState(PAIR_CONFIRM_STATE_USER_CONFIRM);
remoteDevice->SetPairConfirmType(reqType);
int remoteIo = remoteDevice->GetIoCapability();
if (remoteDevice->GetPairedStatus() == PAIR_CANCELING) {
UserConfirmAutoReply(device, reqType, false);
} else if (CheckAutoReply(remoteIo, localMitmRequired, remoteMitmRequired) == true) {
UserConfirmAutoReply(device, reqType, true);
} else {
reqType = CheckSspConfirmType(remoteIo, reqType);
SendPairConfirmed(device, reqType, number);
}
}
ClassicAdapter::SSPConfirmReq函數(shù)取出本設(shè)備及對(duì)端設(shè)備的IOCapability,調(diào)用CheckAutoReply函數(shù)結(jié)合認(rèn)證信息進(jìn)行最終的綜合判斷:如果是自動(dòng)配對(duì),則由ClassicAdapter::SSPConfirmReq調(diào)用UserConfirmAutoReply直接確認(rèn);否則向用戶顯示確認(rèn)信息,要求用戶確認(rèn)。
bool ClassicAdapter::CheckAutoReply(int remoteIo, int localMitmRequired, int remoteMitmRequired) const
{
HILOGI("enter");
bool autoReply = false;
int localIo = adapterProperties_.GetIoCapability();
HILOGI("local io capability = %{public}d <==> remote io capability = %{public}d"
"local mitm = %{public}d <==> remote mitm = %{public}d", localIo, remoteIo,
localMitmRequired, remoteMitmRequired);
if (localMitmRequired == GAP_MITM_NOT_REQUIRED && remoteMitmRequired == GAP_MITM_NOT_REQUIRED) {
return true;
}
switch (localIo) {
case GAP_IO_DISPLAYONLY:
autoReply = (remoteIo != GAP_IO_KEYBOARDONLY) ? true : false;
break;
case GAP_IO_KEYBOARDONLY:
autoReply = (remoteIo == GAP_IO_NOINPUTNOOUTPUT) ? true : false;
break;
case GAP_IO_NOINPUTNOOUTPUT:
autoReply = true;
break;
default:
break;
}
return autoReply;
}
總結(jié)
本文介紹了藍(lán)牙協(xié)議中SSP認(rèn)證配對(duì)過(guò)程及OpenHarmony中相關(guān)實(shí)現(xiàn)流程,藍(lán)牙配對(duì)時(shí)是否會(huì)出現(xiàn)用戶確認(rèn)提示信息依賴兩端設(shè)備能力,同時(shí)也依賴業(yè)務(wù)對(duì)安全性的要求;如果業(yè)務(wù)本身有其它傳輸加密能力,可以指定不認(rèn)證方式進(jìn)行連接,避免用戶多次認(rèn)證導(dǎo)致降低使用體驗(yàn),如OpenHarmony軟總線就是采用這種方式建立藍(lán)牙連接。