精华内容
下载资源
问答
  •  蓝牙配对过程,其实就是一个的过程。  为什么不配对便无法建立连接?  任何无线通信技术都存在被监听和破解的可能,蓝牙SIG为了保证蓝牙通信的安全性,采用的方式进行数据交互。同时为了保证使用的方便性,以...
  •  蓝牙配对过程,其实就是一个认证的过程。  为什么不配对便无法建立连接?  任何无线通信技术都存在被监听和破解的可能,蓝牙SIG为了保证蓝牙通信的安全性,采用认证的方式进行数据交互。同时为了保证使用的...
  • 蓝牙配对过程分析

    2020-08-23 14:40:48
    1.概念 配对是指“Master和Slave通过协商确立用于加(解)密的key的过程。指的是下图中的第7部分。 2.配对方法 Master和Slave有两种可选的配对方法:legacy pairing和Secure Connections。...配对过程如图所示,

    1.概念

    配对是指“Master和Slave通过协商确立用于加(解)密的key的过程。指的是下图中的第7部分。
    蓝牙配对过程分析

    2.配对方法

    Master和Slave有两种可选的配对方法:legacy pairing和Secure Connections。从命名上看,前者是过去的方法,后者是新方法。选择的依据是:当Master和Slave都支持Secure Connections(新方法)的时候,则使用Secure Connections。否则,使用legacy pairing。

    3.配对过程

    配对过程如图所示,主要由下面四部分完成:
    蓝牙配对过程分析

    3.1.Pairing Feature Exchange

    用于交换双方有关鉴权的需求(authentication requirements),以及双方具有怎么的人机交互能力(IO capabilities)。其中最重要的是IO capabilities exchange。
    蓝牙配对过程分析
    IO的能力可以归纳为如下的六种:
    NoInputNoOutput
    DisplayOnly
    NoInputNoOutput1
    DisplayYesNo
    KeyboardOnly
    KeyboardDisplay
    上述的IO能力决定了后续的鉴权方式。

    3.2.Public key exchange

    两个设备之间交换Public key。 一旦设备收到对端设备的公钥,它就可以开始计算Diffie Hellman密钥(DHKey)。耗时较多,应该尽早开始,以便用户交互可以隐藏计算时间。 在步骤8之前不需要DHKey。
    当 Public key的长度大于DM1包的长度时,要使用专门的PDU来进行数据发送。
    蓝牙配对过程分析

    3.3.Authentication

    通过SMP协议进行实际的配对操作,根据阶段1 “Feature Exchange”的结果,有三种鉴权方法可选:

    3.3.1.OOB鉴权:

    如果双方都支持OOB鉴权,则选择该方式(优先级最高)。由配对的双方,在配对过程之外,额外的交互一些信息,并以这些信息为输入,进行后续的配对操作。这些额外信息也称作OOB(out of band),OOB的交互过程称为OOB protocol。
    蓝牙配对过程分析

    3.3.2.MITM鉴权:

    (man-in-the-middle)authentication,由两者方法:
    Numeric Comparision方式鉴权:两个设备自行协商生成6个数字,并显示出来(要求两个设备具有显示能力),用户比较后进行确认(一致,或者不一致,要求设备有简单的yes or no的确认能力)。
    蓝牙配对过程分析
    Passkey Entry,通过输入配对码的方式鉴权。
    蓝牙配对过程分析

    3.3.3.Just Work:

    Just Work,不需要用户参与,两个设备自行协商。

    3.4.DHKey Checks

    一旦设备完成鉴权过程,并且DHKey计算已完成,则检查生成的DHKey值。 如果成功,则两个设备都将完成向用户显示关于该过程的信息,否则控制器向主机发送消息以通知其停止显示该信息。
    蓝牙配对过程分析

    当配对过程完成后,link key就可以从DHKey中计算得到,并用做后续交互过程的输入(KEY + 明文 => 加密数据),通过HCI_Link_Key_Notification来通知host。
    蓝牙配对过程分析

    经过上述过程后,双方已经产生了加密key,因而可以建立加密的连接。加密连接建立后,可以互相传送一些私密的信息,例如Encryption Information、Identity Information、Identity Address Information等。

    展开全文
  • 蓝牙配对过程分析(经典蓝牙)

    千次阅读 2020-04-15 14:49:55
    安全简易配对SSP(Secure simple pairing),蓝牙2.0之后配对方式,简易安全配对一共有四种,其中Out of Band很少使用到,具体如下: Numeric Comparison 配对双方都显示一个6位的数字,由用户来核对数字是否一...

    打开手机/手表的蓝牙,就能在列表中看到扫描到的蓝牙,点击就可以实现配对功能。目前手表支持与所有设备的配对,但是仅支持与耳机类型的设备进行连接

    安全简易配对SSP(Secure simple pairing),蓝牙2.0之后配对方式,简易安全配对一共有四种,其中Out of Band很少使用到,具体如下:
    Numeric Comparison
    配对双方都显示一个6位的数字,由用户来核对数字是否一致,并输入Yes/No,两端Yes表示一致即可配对,可以防止中间人攻击。
    使用场景:两端设备可以弹出6位十进制数,并且有yes和no按钮。

    Passkey Entry
    配对目标输入一个在本地设备上显示的6位数字,输入正确即可配对,并可以防止中间人攻击。
    使用场景:一端设备可以显示,另一端设备可以输入。

    Just Works
    不会进行鉴权,不能防止中间人攻击用于配对没有显示没有输入的设备,主动发起连接即可配对,用户看不到配对过程,不可以防止中间人攻击,例如连接蓝牙耳机。
    使用场景:用于即不能显示6位随机数,也不能输入的设备。

    Out of Band
    两设备的通过别的途径交换配对信息,例如一些NFC蓝牙音箱。

    一、原生应用设置界面的packages\Settings
    目录:android\packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDevicePreference.java

    void onClicked() {
        int bondState = mCachedDevice.getBondState();

        if (mCachedDevice.isConnected()) {
            askDisconnect();//断开连接
        } else if (bondState == BluetoothDevice.BOND_BONDED) {
            mCachedDevice.connect(true);//已经配对了就连接
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            pair();//没有配对的话就去配对
        }
    }

    private void pair() {
        if (!mCachedDevice.startPairing()) {
            Utils.showError(getContext(), mCachedDevice.getName(),
                    R.string.bluetooth_pairing_error_message);
        } else {
            final Context context = getContext();

            SearchIndexableRaw data = new SearchIndexableRaw(context);
            data.className = BluetoothSettings.class.getName();
            data.title = mCachedDevice.getName();
            data.screenTitle = context.getResources().getString(R.string.bluetooth_settings);
            data.iconResId = R.drawable.ic_settings_bluetooth;
            data.enabled = true;

            Index.getInstance(context).updateFromSearchIndexableData(data);
        }
    }

    Y:\HLOS\frameworks\base\packages\SettingsLib\src\com\android\settingslib\bluetooth\CachedBluetoothDevice.java

    public boolean startPairing() {
            // Pairing is unreliable while scanning, so cancel discovery
            if (mLocalAdapter.isDiscovering()) {
                mLocalAdapter.cancelDiscovery();//如果设备正在搜索,就取消搜索
            }

            if (!mDevice.createBond()) {//配对
                return false;
            }

            mConnectAfterPairing = true;  // auto-connect after pairing
            return true;
        }

    二、framework层配对逻辑
    Y:\HLOS\frameworks\base\core\java\android\bluetooth\BluetoothDevice.java

    public boolean createBond() {
            if (sService == null) {
                Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device");
                return false;
            }
            try {
                Log.i(TAG, "createBond() for device " + getAddress() +
                        " called by pid: " + Process.myPid() +
                        " tid: " + Process.myTid());
                return sService.createBond(this, TRANSPORT_AUTO);//跨进程调用到AdapterService中的方法
            } catch (RemoteException e) {Log.e(TAG, "", e);}
            return false;
        }

    三、frameworks的aidl文件
    private static IBluetooth sService;
    1
    HLOS\frameworks\base\core\java\android\bluetooth\IBluetooth.aidl
    boolean createBond(in BluetoothDevice device);
    这个是属于跨进程通信的方法,通过IBluetooth.aidl文件,调用到AdapterService.java中的createBond方法

    四、从framework跨进程调到packages\Bluetooth
    Y:\HLOS\packages\apps\Bluetooth\src\com\android\bluetooth\btservice\AdapterService.java

    public boolean createBond(BluetoothDevice device, int transport) {
                if (!Utils.checkCallerAllowManagedProfiles(mService)) {
                    Log.w(TAG, "createBond() - Not allowed for non-active user");
                    return false;
                }

                AdapterService service = getService();
                if (service == null) return false;
                return service.createBond(device, transport, null);//调用本服务的createBond
            }

    同在AdapterService文件下

    boolean createBond(BluetoothDevice device, int transport, OobData oobData) {
            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                "Need BLUETOOTH ADMIN permission");
            DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
            if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
                return false;
            }
            // Multicast: Do not allow bonding while multcast
            A2dpService a2dpService = A2dpService.getA2dpService();
            if (a2dpService != null &&
                a2dpService.isMulticastFeatureEnabled() &&
                a2dpService.isMulticastOngoing(null)) {
                Log.i(TAG,"A2dp Multicast is ongoing, ignore bonding");
                return false;
            }

            // Pairing is unreliable while scanning, so cancel discovery
            // Note, remove this when native stack improves
            cancelDiscoveryNative();//配对过程,取消扫描

            Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
            msg.obj = device;
            msg.arg1 = transport;

            if (oobData != null) {
                Bundle oobDataBundle = new Bundle();
                oobDataBundle.putParcelable(BondStateMachine.OOBDATA, oobData);
                msg.setData(oobDataBundle);
            }
            mBondStateMachine.sendMessage(msg);//给配对的状态机发消息,创建创建了BondStateMachine.CREATE_BOND
            return true;
        }

    在Y:\HLOS\packages\apps\Bluetooth\src\com\android\bluetooth\btservice\BondStateMachine.java
    处理服务发送过来的消息

    @Override
            public boolean processMessage(Message msg) {

                BluetoothDevice dev = (BluetoothDevice)msg.obj;
                DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
                boolean result = false;
                 if (mDevices.contains(dev) && msg.what != CANCEL_BOND &&
                       msg.what != BONDING_STATE_CHANGE && msg.what != SSP_REQUEST &&
                       msg.what != PIN_REQUEST) {
                     deferMessage(msg);
                     return true;
                 }

                Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);

                switch (msg.what) {
                    case CREATE_BOND:
                        OobData oobData = null;
                        if (msg.getData() != null)
                            oobData = msg.getData().getParcelable(OOBDATA);

                        result = createBond(dev, msg.arg1, oobData, false);
                        break;
                        、、、
                        }
                }

    private boolean createBond(BluetoothDevice dev, int transport, OobData oobData,
                                   boolean transition) {
            if(mAdapterService == null) return false;
            if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
                infoLog("Bond address is:" + dev);
                byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
                boolean result;
                if (oobData != null) {//判读是否借助其他硬件进行无绑定配对
                    result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData);
                } else {
                    result = mAdapterService.createBondNative(addr, transport);//调用到JNI层,进行配对
                }

                if (!result) {
                    sendIntent(dev, BluetoothDevice.BOND_NONE,
                               BluetoothDevice.UNBOND_REASON_REMOVED);
                    return false;
                } else if (transition) {
                    transitionTo(mPendingCommandState);
                }
                return true;
            }
            return false;
        }

    五、packages\Bluetooth的java层调到jini层的cpp文件
    Y:\HLOS\packages\apps\Bluetooth\jni\com_android_bluetooth_btservice_AdapterService.cpp

    static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport) {
        ALOGV("%s:",__FUNCTION__);

        jbyte *addr;
        jboolean result = JNI_FALSE;

        if (!sBluetoothInterface) return result;

        addr = env->GetByteArrayElements(address, NULL);
        if (addr == NULL) {
            jniThrowIOException(env, EINVAL);
            return result;
        }

        int ret = sBluetoothInterface->create_bond((bt_bdaddr_t *)addr, transport);//该接口调用到hal层的配对函数
        env->ReleaseByteArrayElements(address, addr, 0);
        result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;

        return result;
    }

    六、hal层createBond的调用
    Y:\HLOS\system\bt\btif\src\bluetooth.c

    static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
    {
        /* sanity check */
        if (interface_ready() == FALSE)
            return BT_STATUS_NOT_READY;

        return btif_dm_create_bond(bd_addr, transport);
    }

    Y:\HLOS\system\bt\btif\src\btif_dm.c

    bt_status_t btif_dm_create_bond(const bt_bdaddr_t *bd_addr, int transport)
    {
        btif_dm_create_bond_cb_t create_bond_cb;
        create_bond_cb.transport = transport;
        bdcpy(create_bond_cb.bdaddr.address, bd_addr->address);

        bdstr_t bdstr;
        BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __FUNCTION__, bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)), transport);
        if (pairing_cb.state != BT_BOND_STATE_NONE)
            return BT_STATUS_BUSY;

        btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_CREATE_BOND, pairing_cb.state);//添加了绑定事件

        btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_CREATE_BOND,
                              (char *)&create_bond_cb, sizeof(btif_dm_create_bond_cb_t), NULL);//cotext是开辟了上下文的意思,这里create_bond_cb已经包含了要绑定的蓝牙地址,会分别发送给底层两部分,最后会调用btif_dm_generic_evt

        return BT_STATUS_SUCCESS;
    }

    Y:\HLOS\system\bt\btif\src\btif_dm.c

    static void btif_dm_generic_evt(UINT16 event, char* p_param)
    {
        BTIF_TRACE_EVENT("%s: event=%d", __FUNCTION__, event);
        switch(event)
        {
            case BTIF_DM_CB_DISCOVERY_STARTED:
            {
                HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb, BT_DISCOVERY_STARTED);
            }
            break;

            case BTIF_DM_CB_CREATE_BOND://走这一路去配对
            {
                pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES;
                btif_dm_create_bond_cb_t *create_bond_cb = (btif_dm_create_bond_cb_t*)p_param;
                btif_dm_cb_create_bond(&create_bond_cb->bdaddr, create_bond_cb->transport);
            }
            break;

            case BTIF_DM_CB_REMOVE_BOND:
            {
                btif_dm_cb_remove_bond((bt_bdaddr_t *)p_param);
            }
            break;
            、、、
        }
        、、、
    }

    static void btif_dm_cb_create_bond(bt_bdaddr_t *bd_addr, tBTA_TRANSPORT transport)
    {
        BOOLEAN is_hid = check_cod(bd_addr, COD_HID_POINTING);
        bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);

    #if BLE_INCLUDED == TRUE
        int device_type;
        int addr_type;
        bdstr_t bdstr;
        bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr));
        if (transport == BT_TRANSPORT_LE)
        {
            if (!btif_config_get_int((char const *)&bdstr,"DevType", &device_type))
            {
                btif_config_set_int(bdstr, "DevType", BT_DEVICE_TYPE_BLE);
            }
            if (btif_storage_get_remote_addr_type(bd_addr, &addr_type) != BT_STATUS_SUCCESS)
            {
                btif_storage_set_remote_addr_type(bd_addr, BLE_ADDR_PUBLIC);
            }
        }
        if((btif_config_get_int((char const *)&bdstr,"DevType", &device_type) &&
           (btif_storage_get_remote_addr_type(bd_addr, &addr_type) == BT_STATUS_SUCCESS) &&
           (device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) || (transport == BT_TRANSPORT_LE))
        {
            BTA_DmAddBleDevice(bd_addr->address, addr_type, device_type);
        }
    #endif

    #if BLE_INCLUDED == TRUE
        if(is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0)
    #else
        if(is_hid)
    #endif
        {
            int status;
            status = btif_hh_connect(bd_addr);//连接?
            if(status != BT_STATUS_SUCCESS)
                bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);//绑定状态发生变化时,这里开始回调
        }
        else
        {
            BTA_DmBondByTransport((UINT8 *)bd_addr->address, transport);//第一次调用会走这里
        }
        /*  Track  originator of bond creation  */
        pairing_cb.is_local_initiated = TRUE;

    }


    Y:\HLOS\system\bt\bta\dm\bta_dm_api.c

    void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport)
    {
        tBTA_DM_API_BOND *p_msg =
            (tBTA_DM_API_BOND *)osi_malloc(sizeof(tBTA_DM_API_BOND));

        p_msg->hdr.event = BTA_DM_API_BOND_EVT;
        bdcpy(p_msg->bd_addr, bd_addr);
        p_msg->transport = transport;//消息包装

        bta_sys_sendmsg(p_msg);//发送到另一个线程
    }

    七、蓝牙协议栈给线程间消息的收发
    bta_sys_sendmsg这是蓝牙协议栈的进程间收发消息的机制,不是三言两语能说清楚的,若想了解,可以参考android bluedroid协议栈里面的各个组件之间的消息处理机制
    根据BTA_DM_API_BOND_EVT这个类型,可以找到消息接收的地方
    Y:\HLOS\system\bt\bta\dm\bta_dm_main.c

    const tBTA_DM_ACTION bta_dm_action[] =
    {

        /* device manager local device API events */
        bta_dm_enable,            /* 0  BTA_DM_API_ENABLE_EVT */
        bta_dm_disable,           /* 1  BTA_DM_API_DISABLE_EVT */
        bta_dm_set_dev_name,      /* 2  BTA_DM_API_SET_NAME_EVT */
        bta_dm_set_visibility,    /* 3  BTA_DM_API_SET_VISIBILITY_EVT */
        bta_dm_acl_change,        /* 8  BTA_DM_ACL_CHANGE_EVT */
        bta_dm_add_device,        /* 9  BTA_DM_API_ADD_DEVICE_EVT */
        bta_dm_close_acl,        /* 10  BTA_DM_API_ADD_DEVICE_EVT */

        /* security API events */
        bta_dm_bond,              /* 11  BTA_DM_API_BOND_EVT */
        bta_dm_bond_cancel,       /* 12  BTA_DM_API_BOND_CANCEL_EVT */
        bta_dm_pin_reply,         /* 13 BTA_DM_API_PIN_REPLY_EVT */

        /* power manger events */
        bta_dm_pm_btm_status,     /* 16 BTA_DM_PM_BTM_STATUS_EVT */
        bta_dm_pm_timer,          /* 17 BTA_DM_PM_TIMER_EVT*/

        /* simple pairing events */
        bta_dm_confirm,           /* 18 BTA_DM_API_CONFIRM_EVT */

        bta_dm_set_encryption,    /* BTA_DM_API_SET_ENCRYPTION_EVT */

        /* out of band pairing events */
        bta_dm_loc_oob,           /* 20 BTA_DM_API_LOC_OOB_EVT */
        bta_dm_ci_io_req_act,     /* 21 BTA_DM_CI_IO_REQ_EVT */
        bta_dm_ci_rmt_oob_act,    /* 22 BTA_DM_CI_RMT_OOB_EVT */
    、、、
    }

    Y:\HLOS\system\bt\bta\dm\bta_dm_act.c

    void bta_dm_bond (tBTA_DM_MSG *p_data)
    {
        tBTM_STATUS status;
        tBTA_DM_SEC sec_event;
        char        *p_name;

        if (p_data->bond.transport == BTA_TRANSPORT_UNKNOWN)//这里看transport类型,走不通渠道
            status = BTM_SecBond ( p_data->bond.bd_addr, 0, NULL, 0 );
        else
            status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, 0, NULL, 0 );


        if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED))
        {

            memset(&sec_event, 0, sizeof(tBTA_DM_SEC));
            bdcpy(sec_event.auth_cmpl.bd_addr, p_data->bond.bd_addr);
            p_name = BTM_SecReadDevName(p_data->bond.bd_addr);
            if (p_name != NULL)
            {
                memcpy(sec_event.auth_cmpl.bd_name, p_name, (BD_NAME_LEN-1));
                sec_event.auth_cmpl.bd_name[BD_NAME_LEN-1] = 0;
            }

    /*      taken care of by memset [above]
            sec_event.auth_cmpl.key_present = FALSE;
            sec_event.auth_cmpl.success = FALSE;
    */
            sec_event.auth_cmpl.fail_reason = HCI_ERR_ILLEGAL_COMMAND;
            if (status == BTM_SUCCESS)
            {
                sec_event.auth_cmpl.success = TRUE;
            }
            else
            {
                /* delete this device entry from Sec Dev DB */
                bta_dm_remove_sec_dev_entry(p_data->bond.bd_addr);
            }
            bta_dm_cb.p_sec_cback(BTA_DM_AUTH_CMPL_EVT, &sec_event);
        }

    }

    Y:\HLOS\system\bt\stack\btm\btm_sec.c

    tBTM_STATUS BTM_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
                                        UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
    {
    #if SMP_INCLUDED == TRUE
        tBT_DEVICE_TYPE     dev_type;
        tBLE_ADDR_TYPE      addr_type;

        BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
        /* LE device, do SMP pairing */
        if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) ||
            (transport == BT_TRANSPORT_BR_EDR && (dev_type & BT_DEVICE_TYPE_BREDR) == 0))
        {
            return BTM_ILLEGAL_ACTION;
        }
    #endif
        return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask);
    }

    Y:\HLOS\system\bt\stack\btm\btm_sec.c

    tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
                                           UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
    {
        tBTM_SEC_DEV_REC *p_dev_rec;
        tBTM_STATUS      status;
        UINT8            *p_features;
        UINT8            ii;
        tACL_CONN        *p= btm_bda_to_acl(bd_addr, transport);
        BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x",
                        bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);

        BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d" , transport);

        /* Other security process is in progress */
        if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
        {
            BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state));
            return(BTM_WRONG_MODE);
        }

        if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL)
        {
            return(BTM_NO_RESOURCES);
        }
        if (!controller_get_interface()->get_is_ready())
        {
            BTM_TRACE_ERROR ("%s controller module is not ready", __func__);
            return(BTM_NO_RESOURCES);
        }
        BTM_TRACE_DEBUG ("before update sec_flags=0x%x", p_dev_rec->sec_flags);

        /* Finished if connection is active and already paired */
        if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR
             &&  (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))
    #if (BLE_INCLUDED == TRUE)
            ||((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE
             &&  (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))
    #endif

             )
        {
            BTM_TRACE_WARNING("BTM_SecBond -> Already Paired");
            return(BTM_SUCCESS);
        }

        /* Tell controller to get rid of the link key if it has one stored */
        if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS)
            return(BTM_NO_RESOURCES);

        /* Save the PIN code if we got a valid one */
        if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0))
        {
            btm_cb.pin_code_len = pin_len;
            p_dev_rec->pin_code_length = pin_len;
            memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN);
        }

        memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN);

        btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD;

        p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;
        p_dev_rec->is_originator     = TRUE;
        if (trusted_mask)
            BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);

    #if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
        if (transport == BT_TRANSPORT_LE)
        {
            btm_ble_init_pseudo_addr (p_dev_rec, bd_addr);
            p_dev_rec->sec_flags &= ~ BTM_SEC_LE_MASK;

            if (SMP_Pair(bd_addr) == SMP_STARTED)
            {
                btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE;
                p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
                btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
                return BTM_CMD_STARTED;
            }

            btm_cb.pairing_flags = 0;
            return(BTM_NO_RESOURCES);
        }
    #endif

        p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED
                                      | BTM_SEC_ROLE_SWITCHED  | BTM_SEC_LINK_KEY_AUTHED);

        BTM_TRACE_DEBUG ("after update sec_flags=0x%x", p_dev_rec->sec_flags);
        if (!controller_get_interface()->supports_simple_pairing())
        {
            /* The special case when we authenticate keyboard.  Set pin type to fixed */
            /* It would be probably better to do it from the application, but it is */
            /* complicated */
            if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL)
                && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)
                && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED))
            {
                btm_cb.pin_type_changed = TRUE;
                btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED);
            }
        }

        for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++)
        {
            p_features = p_dev_rec->features[ii];
            BTM_TRACE_EVENT("  remote_features page[%1d] = %02x-%02x-%02x-%02x",
                             ii, p_features[0], p_features[1], p_features[2], p_features[3]);
            BTM_TRACE_EVENT("                              %02x-%02x-%02x-%02x",
                                 p_features[4], p_features[5], p_features[6], p_features[7]);
        }

        BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x  HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle);

    #if BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE
        p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN;
    #endif

        /* If connection already exists... */
        if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE)
        {
            if (!btm_sec_start_authentication (p_dev_rec))
                return(BTM_NO_RESOURCES);

            btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);

            /* Mark lcb as bonding */
            l2cu_update_lcb_4_bonding (bd_addr, TRUE);
            return(BTM_CMD_STARTED);
        }

        BTM_TRACE_DEBUG ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);
        if (!controller_get_interface()->supports_simple_pairing()
            || (p_dev_rec->sm4 == BTM_SM4_KNOWN))
        {
            if ( btm_sec_check_prefetch_pin (p_dev_rec) )
                return (BTM_CMD_STARTED);
        }
        if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
             btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
             btm_cb.security_mode == BTM_SEC_MODE_SC) &&
             BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4))
        {
            /* local is 2.1 and peer is unknown */
            if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0)
            {
                /* we are not accepting connection request from peer
                 * -> RNR (to learn if peer is 2.1)
                 * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
                btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME);
                BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
            }
            else
            {
                /* We are accepting connection request from peer */
                btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);
            }
            BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d",
                btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state);
            return BTM_CMD_STARTED;
        }

        /* both local and peer are 2.1  */
        status = btm_sec_dd_create_conn(p_dev_rec);

        if (status != BTM_CMD_STARTED)
        {
            btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);//改变配对的状态
        }

        return status;
    }

    这里代码很多,每一句都看懂不现实,现摘出重要的一段看

     if (!controller_get_interface()->supports_simple_pairing())//这里做一个判断,看是否支持简单配对方式
        {
            /* The special case when we authenticate keyboard.  Set pin type to fixed */
            /* It would be probably better to do it from the application, but it is */
            /* complicated */
            if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL)
                && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)
                && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED))
            {
                btm_cb.pin_type_changed = TRUE;
                btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED);//这里就可以看到在和hci层打交道了
            }
        }

    八、通过HCI向底层发送命令进行控制
    BOOLEAN btsnd_hcic_write_pin_type (UINT8 type)
    {
        BT_HDR *p = (BT_HDR *)osi_malloc(HCI_CMD_BUF_SIZE);
        UINT8 *pp = (UINT8 *)(p + 1);

        p->len    = HCIC_PREAMBLE_SIZE + HCIC_PARAM_SIZE_WRITE_PARAM1;
        p->offset = 0;

        UINT16_TO_STREAM (pp, HCI_WRITE_PIN_TYPE);
        UINT8_TO_STREAM  (pp, HCIC_PARAM_SIZE_WRITE_PARAM1);

        UINT8_TO_STREAM (pp, type);

        btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID,  p);//这里是向hci层发命令,
        return (TRUE);
    }

    可以看出,这里是通过和hci层的通信,host告诉controlor蓝牙地址、数据、命令等,从而控制其底层硬件发起配对操作
    ————————————————
    版权声明:本文为CSDN博主「跳舞的蘑菇」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/w1107845086/article/details/79636090

    展开全文
  • NFC蓝牙配对方案

    热门讨论 2013-03-08 17:18:11
    TI的NCF自动配对蓝牙方案demo,介绍了NFC握手过程以及蓝牙配对过程
  • 二、蓝牙配对过程 三、蓝牙配对方式展示 一、蓝牙配对介绍 蓝牙规范定义了两种标准配对过程,LMP配对(也称为基于PIN码)和SSP安全简易配对。 LMP(LinkManager Protocol)配对,又名PIN码配对,即pincode配对方式...

    目录

    一、蓝牙配对介绍

    二、蓝牙配对过程

    三、蓝牙配对方式展示


    一、蓝牙配对介绍

    蓝牙规范定义了两种标准配对过程,LMP配对(也称为基于PIN码)和SSP安全简易配对。

    LMP(Link Manager Protocol)配对,又名PIN码配对,即pincode配对方式。

    SSP(Secure simple pairing)安全简易配对,一共有四种,其中Out of Band很少使用到,具体如下:

    (1)Numeric Comparison (即confirm配对方式)

    配对双方都显示一个6位的数字,由用户来核对数字是否一致,并输入Yes/No,两端Yes表示一致即可配对,可以防止中间攻击。 使用场景:两端设备可以弹出6位十进制数,并且有yes和no按钮。

    (2)Passkey Entry 

    配对目标输入一个在本地设备上显示的6位数字,输入正确即可配对,并可以防止中间人攻击。 使用场景:一端设备可以显示,另一端设备可以输入。

    (3)Just Works 

    不会进行鉴权,不能防止中间人攻击用于配对没有显示没有输入的设备,主动发起连接即可配对,用户看不到配对过程,不可以防止中间人攻击,例如连接蓝牙耳机。 使用场景:用于即不能显示6位随机数,也不能输入的设备。

    (4)Out of Band 

    两设备的通过别的途径交换配对信息,例如一些NFC蓝牙音箱。

    任何配对方法的结果都是一样的:即创建共享的链路密钥(Link Key)。

    二、蓝牙配对过程

    配对设备的过程旨在在两个蓝牙设备之间创建共享密钥:链路密钥(Link Key)。然后,该链路密钥(Link Key)用于彼此认证设备并加密交换的数据。数据实际上并没有使用链路密钥(Link Key)直接加密;临时加密密钥(Encryption Key)从链路密钥(Link Key)和在加密通信流开始之前不久交换的随机数导出。然后,该加密密钥(Encryption Key)用于在两个方向上加密数据。可以在连接处于活动状态的任何时候更改它,一旦连接关闭,或者如果加密停止,将被丢弃。

    一旦两个设备拥有相同的链路密钥(Link Key),该共享密钥可以用于在稍后的时间将两个设备重新认证。当重新连接时,设备通过交换从其派生的数字来快速验证它们都具有相同的链路密钥(Link Key)。如果链路密钥(Link Key)匹配,则可以继续创建会话密钥(Session Key)。否则,配对过程(LMP配对或SSP)必须从一开始重新启动,从而创建一个全新的链路密钥(Link Key)。

    三、蓝牙配对方式展示

    这里主要展示pincode、confirm、passkey和justwork四种配对方式,看看蓝牙设备在不同的配对方式下,在手机设置的系统蓝牙中,配对蓝牙设备,界面弹出的配对框是什么样子的。

    (1)pincode 配对方式,配对码一般为0000或1234,只要手机端配对码输入正确,即可配对成功。

    (2)confirm 配对方式,除了手机端确认配对码之外,蓝牙设备端也需要确认该配对码,两端同时确认正确之后才能配对成功。

    (3)passkey 配对方式,手机端弹出配对码显示框,在蓝牙设备端填入该配对码确认,确认正确才能配对成功。

    (4)justwork 配对方式,手机端、蓝牙设备端不显示任何配对相关信息,直接配对成功。

    参考文章:

    1、蓝牙安全简单配对的解释 https://www.jianshu.com/p/683c287fee3e

    2、蓝牙配对过程分析(经典蓝牙)https://blog.csdn.net/w1107845086/article/details/79636090

     

    展开全文
  • 安卓系统蓝牙配对流程分析配对流程基本上始于首次连接一个蓝牙设备的过程中,本端的搜索流程结束获取到该设备的BluetoothDevice信息,就可以开启配对流程。配对,顾名思义就是将两个设备通过相关技术进行绑定,达到...

    安卓系统蓝牙配对流程分析

    1a943181398041ba3e75f6b753ae54f8.png

    配对流程基本上始于首次连接一个蓝牙设备的过程中,本端的搜索流程结束获取到该设备的BluetoothDevice信息,就可以开启配对流程。

    配对,顾名思义就是将两个设备通过相关技术进行绑定,达到能够互相识别彼此的目的。因此通过蓝牙技术进行的配对就是大家理解中的蓝牙配对。随着蓝牙技术在消费市场上越来越普及,生活中很容易就能遇到蓝牙配对这一使用蓝牙功能是必不可缺失的步骤。本期我们就简单聊聊安卓系统蓝牙配对的流程是如何运行的。

    Application通过framework层BluetoothDevice对外提供的接口createBond() 开启这一流程。createBondOutOfBand() 通过OOB技术进行配对的接口,一般很少使用。所以应用层需要根据自己的实际使用场景选择正确的接口。

    蓝牙服务层在配对流程中基本没有复杂的操作,只是简单地下发指令成功后,通过JNI层的异步回调处理配对状态变化并将该变化对外广播。其时序图如下:

    7ae8c53b1a0f6e8ff814fbfe22303b68.png

    从上图我们唯一需要注意的点就是配对流程开始前需要停掉搜索扫描流程(如果底层正在进行该流程),增加该操作的主要目的是为了确保配对流程能够正常进行。因为搜索扫描和配对流程都会对远端设备进行Discovery操作,防止这两个流程冲突导致配对失败。

    协议栈接收到开始配对指令后,主要按照如下的配对状态机进行切换(NumericComparison模式):

    fe98d8fbb7162f491110f433c419b439.png

    • IDLE :初始状态值

    • GET_REM_NAME :获取远端设备的蓝牙名字

    • WAIT_PIN_REQ :等待Controller请求PIN码

    • WAIT_LOCAL_IOCAPS :等待本端提供IO能力

    • WAIT_NUM_CONFIRM :等待本端Host确认配对

    • WAIT_AUTH_COMPLETE :等待鉴权(配对)流程完成

    状态切换对应到HCI上的交互见下图:

    45829d32b9ffc964404cc52a793d164f.png

    每一个配对状态的切换触发时机从上面这张图就十分明了,这里我就不做过多分析。感兴趣的同学可以尝试跟着上图中HCI命令跟踪下代码流程。

    协议栈的配对状态随着鉴权完成事件 Authentication Complete 的上报就切h换到初始值了,那这时是不是就代表整个配对流程完成了呢?非也,其实在安卓系统中到这里还不代表配对已经完成,因为上报JNI层的配对状态变化回调并不是通过鉴权完成事件上报完成而回调的。

    配对流程的主要工作完成后,协议栈还会继续对该设备尝试进行SDP服务发现,搜索该设备支持哪些协议服务,SDP服务搜索完成后才会上报JNI层配对状态变成Bonded。

    触发SDP服务搜索的触发时机是处理上报LinkKey的回调bta_dm_new_link_key_cback()中通过 event = BTA_DM_AUTH_CMPL_EVT 触发的。

    随后在函数btif_dm_auth_cmpl_evt()中尝试对该设备进行SDP服务发现,如下图:

    f7ad44fdf151c372aae76652c09da922.png

    SDP服务发现完成后通过回调处理函数btif_dm_search_services_evt()中event = BTA_DM_DISC_RES_EVT上报JNI 层配对状态变化到Bonded。

    至此安卓系统中蓝牙配对流程才算闭环完成,上述过程我以Numeric Comparison 配对模型为依据介绍了整个流程,其他配对模型类似,就不一一做出说明了,但协议栈配对状态机的全部值如下,其他模型的配对流程也是在这几种状态机组合下进行切换运行的:

    enum { BTM_PAIR_STATE_IDLE,                   /*Idle */ BTM_PAIR_STATE_GET_REM_NAME,           /*Getting the remote name (tocheck for SM4) */ BTM_PAIR_STATE_WAIT_PIN_REQ,           /*Started authentication, waiting for PIN req (PIN is pre-fetched) */ BTM_PAIR_STATE_WAIT_LOCAL_PIN,         /* Waiting for local PIN code*/ BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM,   /*Waiting user 'yes' to numeric confirmation */ BTM_PAIR_STATE_KEY_ENTRY,              /*Key entry state (we are a keyboard) */ BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP,     /*Waiting for local response to peer OOB data*/ BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS,      /*Waiting for local IO capabilities and OOB data */ BTM_PAIR_STATE_INCOMING_SSP,           /*Incoming SSP (got peer IO caps when idle) */ BTM_PAIR_STATE_WAIT_AUTH_COMPLETE,     /*All done, waiting authentication cpmplete */ BTM_PAIR_STATE_WAIT_DISCONNECT         /* Waiting to disconnect theACL */};

    配对流程实际上并不是孤立而存在的,配合着搜索扫描、连接等流程共同起作用的,彼此依赖,缺一不可。

    本期的分享就到这里,感兴趣的小伙伴欢迎私信留言一起讨论。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 270
精华内容 108
关键字:

蓝牙配对过程