1

OpenHarmony蓝牙自动配对流程分析

 8 months ago
source link: https://www.51cto.com/article/768976.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
a75abf60249f8461965056a73b52fda9e0afcc.png

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com

大家在实际使用蓝牙时会发现,有些蓝牙设备配对需要输入配对码,有些蓝牙设备则会自动配对;那这些设备有什么区别,OpenHarmony的蓝牙协议栈又是怎么实现的呢?本文对此进行分析和解读。

蓝牙协议分析

SSP(SECURE SIMPLE PAIRING)时当前蓝牙协议中最推荐采用的认证配对方案;在SSP配对模式下,认证配对总体分为两步:IO Capability信息交换和用户确认。

IO Capability

蓝牙设备按照输入输出能力分为四类,以设备A作Initiator组合后认证配置方式如下表:

设备B\设备A

DisplayOnly

DisplayYesNo

KeyboardOnly

NoInputNoOutput

DisplayOnly

A用户确认,B自动配对

B显示数字,A输入

DisplayYesNo

A自动配对,B用户确认

B显示数字,A输入

A自动配对,B用户确认

KeyboardOnly

A显示数字,B输入

A显示数字,B输入

输入passkey

NoInputNoOutput

B自动配对,A用户确认

交换设备IO Capability信息流程如下图:

resize,w_820,h_656

MITM Protection

参考蓝牙core specification Version 5.4 | Vol 4, Part E, 7.1.29,可以发现IO Capability消息中除了IO_Capability字段还包括Authentication_Requirements字段,该字段同样影响设备配对流程。

resize,w_768,h_173

man-in-the-middle(MITM) ,中间人攻击是一种常见的攻击手法,蓝牙SSP机制在用户确认模式时可以有效防止中间人攻击。

协议规定:如果两台设备都明确指定不需要进行MITM攻击保护,设备应该按照自动匹配流程处理。

resize,w_791,h_790

host收到User_Confirmation_Request消息后需要按照上表中的IO Capability要求用户确认或自动回复确认信息。

OpenHarmony实现流程

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函数处理对端设备的IOCapability信息,remoteAuthReq保存对端设备的认证要求;同时在ClassicAdapter模块保存对端设备IOCapability能力;这里比较奇怪的是IOCapability和remoteAuthReq分在两个模块保存。

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);
}
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函数获取到IO Capability交换流程中保存认证要求,并获取本设备最近一次连接的认证设置,作为参数传递到ClassicAdapter::SSPConfirmReq函数进行处理。

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函数取出本设备及对端设备的IOCapability,调用CheckAutoReply函数结合认证信息进行最终的综合判断:如果是自动配对,则由ClassicAdapter::SSPConfirmReq调用UserConfirmAutoReply直接确认;否则向用户显示确认信息,要求用户确认。

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;
}

本文介绍了蓝牙协议中SSP认证配对过程及OpenHarmony中相关实现流程,蓝牙配对时是否会出现用户确认提示信息依赖两端设备能力,同时也依赖业务对安全性的要求;如果业务本身有其它传输加密能力,可以指定不认证方式进行连接,避免用户多次认证导致降低使用体验,如OpenHarmony软总线就是采用这种方式建立蓝牙连接。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK