原始需求:
在dual mode下,客户需要Classic & BLE分别使用不同的名字,这就需要BLE使用RPA,让Classic & BLE成为两个不同的设备。
# Default privacy setting.# Enables use of private address.# Possible values for LE mode: "off", "network/on", "device"# Possible values for Dual mode: "off", "network/on", "device",# "limited-network", "limited-device"## - off: Local privacy disabled.## - network/on: A device will only accept advertising packets from peer# devices that contain private addresses. It may not be compatible with some# legacy devices since it requires the use of RPA(s) all the time.## - device: A device in device privacy mode is only concerned about the# privacy of the device and will accept advertising packets from peer devices# that contain their Identity Address as well as ones that contain a private# address, even if the peer device has distributed its IRK in the past.# - limited-network: Apply Limited Discoverable Mode to advertising, which# follows the same policy as to BR/EDR that publishes the identity address when# discoverable, and Network Privacy Mode for scanning.## - limited-device: Apply Limited Discoverable Mode to advertising, which# follows the same policy as to BR/EDR that publishes the identity address when# discoverable, and Device Privacy Mode for scanning.## Defaults to "off"#Privacy = offBlueZ main.conf中添加“Privacy = device”,使能BLE RPA。
使用手机连接并配对DUT BLE,发现配对失败。
查看kernel中smp回复DH Key Check Failed的代码:
staticintsmp_cmd_dhkey_check(structl2cap_conn*conn,structsk_buff*skb){structsmp_cmd_dhkey_check*check=(void*)skb->data;structl2cap_chan*chan=conn->smp;structhci_conn*hcon=conn->hcon;structsmp_chan*smp=chan->data;u8 a[7],b[7],*local_addr,*remote_addr;u8 io_cap[3],r[16],e[16];interr;bt_dev_dbg(hcon->hdev,"conn %p",conn);if(skb->len<sizeof(*check))returnSMP_INVALID_PARAMS;memcpy(a,&hcon->init_addr,6);memcpy(b,&hcon->resp_addr,6);a[6]=hcon->init_addr_type;b[6]=hcon->resp_addr_type;if(test_bit(SMP_FLAG_INITIATOR,&smp->flags)){local_addr=a;remote_addr=b;memcpy(io_cap,&smp->prsp[1],3);}else{local_addr=b;remote_addr=a;memcpy(io_cap,&smp->preq[1],3);}memset(r,0,sizeof(r));if(smp->method==REQ_PASSKEY||smp->method==DSP_PASSKEY)put_unaligned_le32(hcon->passkey_notify,r);elseif(smp->method==REQ_OOB)memcpy(r,smp->lr,16);err=smp_f6(smp->tfm_cmac,smp->mackey,smp->rrnd,smp->prnd,r,io_cap,remote_addr,local_addr,e);if(err)returnSMP_UNSPECIFIED;if(crypto_memneq(check->e,e,16))returnSMP_DHKEY_CHECK_FAILED;if(!test_bit(SMP_FLAG_INITIATOR,&smp->flags)){if(test_bit(SMP_FLAG_WAIT_USER,&smp->flags)){set_bit(SMP_FLAG_DHKEY_PENDING,&smp->flags);return0;}/* Responder sends DHKey check as response to initiator */sc_dhkey_check(smp);}sc_add_ltk(smp);if(test_bit(SMP_FLAG_INITIATOR,&smp->flags)){hci_le_start_enc(hcon,0,0,smp->tk,smp->enc_key_size);hcon->enc_key_size=smp->enc_key_size;}return0;}可以看到是因为本地smp_f6()算出来的结果与对端发过来的计算结果不一致。
本地kernel smp算法库或者其他配置有问题?
做个对比实验,不配置"Privacy = device",看看是否可以正常连接配对。
可以正常连接+配对,排除smp算法库和本地配置问题。
staticintsmp_f6(structcrypto_shash*tfm_cmac,constu8 w[16],constu8 n1[16],constu8 n2[16],constu8 r[16],constu8 io_cap[3],constu8 a1[7],constu8 a2[7],u8 res[16]){u8 m[65];interr;SMP_DBG("w %16phN",w);SMP_DBG("n1 %16phN n2 %16phN",n1,n2);SMP_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN",r,io_cap,a1,a2);memcpy(m,a2,7);memcpy(m+7,a1,7);memcpy(m+14,io_cap,3);memcpy(m+17,r,16);memcpy(m+33,n2,16);memcpy(m+49,n1,16);err=aes_cmac(tfm_cmac,w,m,sizeof(m),res);if(err)returnerr;SMP_DBG("res %16phN",res);returnerr;}这是smp_f6()算法,除了参数7、8之外,其他都是smp配对过程的中间产物,我们已经排除了算法库&本地配置的问题,接下来可以重点关注a1、a2这两个变数上。
我们追一下smp_f6()的参数7、8:a1、a2,他们是remote_addr + addr_type,local_addr + addr_type组合起来的2个7字节数组。
唯一的可能就是DUT和对端手机smp_f6()算法的a1、a2参数不一致。
这是本端HCI Log中Host设置的RPA:
这是用nRF-Connect搜索到的:
果然不一样,难怪SMP DH Key Check Fail.
HCI LE Set Extended Advertising Parameters Command的Own_Address_Type参数,Spec的描述如下:
看起来是Controller另外生成了一个RPA,生成RPA的条件是Controller的resolving list中包含可以匹配的entry,否则controller广播会使用LE_Set_Advertising_Set_Random_Address命令带下来的address。
但这里controller是如何匹配上了resolving list中的entry的呢?
从开机HCI log中看到两处不合理的地方
1)Host开机就下发了HCI_LE_Add_Device_To_Resolving_List,peer address = local public addr,peer IRK = local IRK
这条命令只能用于添加配对过的设备才对
2)LE_Set_Extended_Advertising_Pramam CMD中Peer Addr = local public addr
只有直连广播才会设置这个参数
搜索kernel代码,发现了下面这段,看起来kernel对privacy的实现卡了一个spec的bug,故意设置[HCI_LE_Add_Device_To_Resolving_List,peer addr = local public addr],且[LE_Set_Extended_Advertising_Pramam CMD,peer addr = local public addr],这使得controller正好能够从resolving list匹配到一个entry,使用这个entry的local IRK来生成一个RPA.
staticinthci_powered_update_adv_sync(structhci_dev*hdev){structadv_info*adv,*tmp;interr;if(!hci_dev_test_flag(hdev,HCI_LE_ENABLED))return0;/* If RPA Resolution has not been enable yet it means the * resolving list is empty and we should attempt to program the * local IRK in order to support using own_addr_type * ADDR_LE_DEV_RANDOM_RESOLVED (0x03). */if(!hci_dev_test_flag(hdev,HCI_LL_RPA_RESOLUTION)){hci_le_add_resolve_list_sync(hdev,NULL);hci_le_set_addr_resolution_enable_sync(hdev,0x01);}。。。。。。/* Call for each tracked instance to be scheduled */list_for_each_entry_safe(adv,tmp,&hdev->adv_instances,list)hci_schedule_adv_instance_sync(hdev,adv->instance,true);return0;}inthci_setup_ext_adv_instance_sync(structhci_dev*hdev,u8 instance){。。。。。。/* If Own_Address_Type equals 0x02 or 0x03, the Peer_Address parameter * contains the peer’s Identity Address and the Peer_Address_Type * parameter contains the peer’s Identity Type (i.e., 0x00 or 0x01). * These parameters are used to locate the corresponding local IRK in * the resolving list; this IRK is used to generate their own address * used in the advertisement. */if(own_addr_type==ADDR_LE_DEV_RANDOM_RESOLVED)hci_copy_identity_address(hdev,&cp.peer_addr,&cp.peer_addr_type);err=hci_set_ext_adv_params_sync(hdev,adv,&cp,&rp);if(err)returnerr;。。。。。。return0;}
好了,一切都说得通了,要想解决这个问题,只需要DUT & 手机计算smp_f6()时,a1、a2参数相匹配就行。那host如何能拿到controller实际广播的RPA呢?
答案就在LE Enhanced Connection Complete Event中:
我们的controller上报的Local RPA参数全为0,所以smp_f6()计算结果匹配失败。只需要controller在这个evt中上报实际使用的RPA,那么这个问题就解决了。
我们来延伸一下,如果使能了bluez privacy = device,应用层如何获取当前controller使用的RPA呢?
spec中有这样一条命令,可以获取local RPA,但是需要填两个参数:peer identity address type + peer identity address,从描述上看是使用已配对设备的地址信息来获取local RPA,但是在没有任何配对信息之前,我们如何获取呢?
我们正好可以利用bluez实现privacy的技巧,peer addr就填local addr,这样就能以在resolving list中匹配到一个entry,拿到local RPA。
在bluez/lib/hci.c中增加接口:
typedefstruct{uint8_tpeer_addr_type;bdaddr_tpeer_addr;}__attribute__((packed))le_read_local_resolvable_addr_cp;typedefstruct{uint8_tstatus;bdaddr_tlocal_rpa;}__attribute__((packed))le_read_local_resolvable_addr_rp;#defineOCF_LE_READ_LOCAL_RESOLVABLE_ADDR0x002C#defineLE_READ_LOCAL_RESOLVABLE_ADDR_CP_SIZEsizeof(le_read_local_resolvable_addr_cp)#defineLE_READ_LOCAL_RESOLVABLE_ADDR_RP_SIZEsizeof(le_read_local_resolvable_addr_rp)inthci_le_read_local_resolvable_address(intdd,uint8_tpeer_addr_type,constbdaddr_t*peer_addr,bdaddr_t*local_rpa,intto){structhci_requestrq;le_read_local_resolvable_addr_cp cp;le_read_local_resolvable_addr_rp rp;if(!peer_addr||!local_rpa){errno=EINVAL;return-1;}memset(&cp,0,sizeof(cp));memset(&rp,0,sizeof(rp));cp.peer_addr_type=peer_addr_type;bacpy(&cp.peer_addr,peer_addr);memset(&rq,0,sizeof(rq));rq.ogf=OGF_LE_CTL;rq.ocf=OCF_LE_READ_LOCAL_RESOLVABLE_ADDR;rq.cparam=&cp;rq.clen=LE_READ_LOCAL_RESOLVABLE_ADDR_CP_SIZE;rq.rparam=&rp;rq.rlen=LE_READ_LOCAL_RESOLVABLE_ADDR_RP_SIZE;if(hci_send_req(dd,&rq,to)<0)return-1;if(rp.status){errno=EIO;return-1;}bacpy(local_rpa,&rp.local_rpa);return0;}在应用层调用这个接口来验证一下:
staticintble_read_local_rpa(void){bdaddr_tlocal_bda,local_rpa;intdd;intdev_id;dev_id=hci_get_route(NULL);if(dev_id<0){AML_LOGI("no default bt adapter \n");returnLM_STATUS_FAIL;}dd=hci_open_dev(dev_id);if(dd<0){AML_LOGE("Failed to open HCI device\n");returnLM_STATUS_FAIL;}hci_devba(dev_id,&local_bda);hci_le_read_local_resolvable_address(dd,0,&local_bda,&local_rpa,1000);hci_close_dev(dd);AML_LOGI("Local BDA %02X:%02X:%02X:%02X:%02X:%02X, Local RPA %02X:%02X:%02X:%02X:%02X:%02X\n",local_bda.b[0],local_bda.b[1],local_bda.b[2],local_bda.b[3],local_bda.b[4],local_bda.b[5],local_rpa.b[0],local_rpa.b[1],local_rpa.b[2],local_rpa.b[3],local_rpa.b[4],local_rpa.b[5]);}(ble_read_local_rpa): Local BDA 2E:2C:75:62:A5:10, Local RPA 88:8B:30:B6:61:47