OpenHarmony蓝牙自动配对流程分析 原创 精华

深开鸿
发布于 2023-10-7 09:40
浏览
2收藏

作者:石悌君

前言

大家在实际使用蓝牙时会发现,有些蓝牙设备配对需要输入配对码,有些蓝牙设备则会自动配对;那这些设备有什么区别,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信息流程如下图

OpenHarmony蓝牙自动配对流程分析-鸿蒙开发者社区

MITM Protection

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

OpenHarmony蓝牙自动配对流程分析-鸿蒙开发者社区

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

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

用户确认

OpenHarmony蓝牙自动配对流程分析-鸿蒙开发者社区

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软总线就是采用这种方式建立蓝牙连接。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
5
收藏 2
回复
举报
4条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

通常蓝牙的传输距离都比较近,还需要输入配对码的应该是对保密程度要求非常高的设备了

回复
2023-10-7 11:50:48
FlashinMiami
FlashinMiami

学习一下蓝牙知识

回复
2023-10-7 13:55:58
wzhishun
wzhishun

这下明白是如何配对的了

回复
2023-10-8 10:33:24
liurick
liurick

get了新知识

回复
2023-10-8 16:12:20
回复
    相关推荐