一、背景

一个 BLE 设备,可以使用两种类型的地址(一个 BLE 设备可同时具备两种地址):

  • Public Device Address(公共设备地址)
  • Random Device Address(随机设备地址)可分为两类:
    • Static Device Address(静态设备地址)
    • Private Device Address(私密设备地址)又可分为两类:
      • Non-resolvable Private Address(不可解析私密地址)
      • Resolvable Private Address(可解析私密地址)

1.1 公共设备地址 Public Device Address

在通信系统中,设备地址是用来唯一识别一个物理设备的,如TCP/IP网络中的MAC地址、传统蓝牙中蓝牙地址等。对设备地址而言,一个重要的特性,就是唯一性。

对于经典蓝牙(BR/EDR)来说,其设备地址是一个 48bits 的数字,称作“48-bit universal LAN MAC address”。正常情况下,该地址需要向 IEEE 申请,具有唯一性。

这种地址分配方式在 BLE 中也保留了下来,就是公共设备地址(Public Device Address)。由 24-bit 的 company_id 和 24-bit 的 company_assigned 组成。

高 24 位是公司标识,低 24 位公司内部自己赋值。

1.2 随机设备地址 Random Device Address

但是,在 BLE 时代,只有公共设备地址明显不够用了,有如下原因:

  1. 公共设备地址需要向 IEEE 购买,需要一笔开销。
  2. 公共设备地址的申请与管理相对繁琐、复杂,再加上 BLE 设备的数量众多(和传统蓝牙设备不是一个数量级的),导致维护成本增大。
  3. 安全因素。BLE 很大一部分的应用场景是广播通信,这意味着只要知道设备的地址,就可以获取所有的信息,这是很不安全的。因此固定的设备地址,加大了信息泄露的风险。

为了解决上述问题,BLE 协议新增了一种地址:随机设备地址,即设备地址不是固定分配的,而是在设备启动后随机生成的。根据不同的目的,随机设备地址分为静态设备地址和私密设备地址。

1.2.1 静态设备地址 Static Device Address

静态设备地址是设备在上电时随机生成的地址,NRF52832 官方工程默认都是使用静态地址,其格式如下:

静态设备地址的特征可总结为:

  1. 最高两个 bit 为 “11”。
  2. 剩余的 46bits 是一个随机数,不能全部为0,也不能全部为1。
  3. 在一个上电周期内保持不变。
  4. 下一次上电的时候可以改变。但不是强制的,因此也可以保持不变。如果改变,上次保存的连接等信息,将不再有效。

静态设备地址的使用场景可总结为:

  1. 46bits 的随机数,可以很好地解决“设备地址唯一性”的问题,因为两个地址相同的概率很小。
  2. 地址随机生成,可以解决公共设备地址申请所带来的费用和维护问题。

1.2.2 私密设备地址 Private Device Address

静态设备地址通过地址随机生成的方式,解决了部分问题。私密设备地址则更进一步,通过定时更新和地址加密两种方式,提高蓝牙地址的可靠性和安全性。根据设备地址是否加密,又分为两类:
① 不可解析私密地址 Non-resolvable Private Address
不可解析私密地址和静态设备地址类似,不同之处在于不可解析私密地址会定时更新。更新的周期是由 GAP 规定的,称作 T_GAP(private_addr_int),建议值是 15 分钟。其格式如下:

不可解析私密地址的特征可总结为:

  1. 最高两个 bit 为 “00”。
  2. 剩余的 46bits 是一个随机数,不能全部为0,也不能全部为1。
  3. 以 T_GAP(private_addr_int) 为周期,定时更新。

② 可解析私密地址 Resolvable Private Address
可解析私密地址比较有用,它通过一个随机数和一个称作 identity resolving key(IRK) 的密码生成,因此只能被拥有相同 IRK 的设备扫描到,可以防止被未知设备扫描和追踪。其格式如下:

可解析私密地址的特征可总结为:

  1. 高位 24bits 是随机数部分,其中最高两个 bit 为“10”,用于标识地址类型;低位 24bits 是随机数和 IRK 经过 hash 运算得到的 hash值,运算公式为 hash = ah(IRK, prand)。
  2. 当主端 BLE 设备扫描到该类型的蓝牙地址后,会使用保存在本机的 IRK,和该地址中的 prand,进行同样的 hash 运算,并将运算结果和地址中的 hash 字段比较,相同的时候,才进行后续的操作。这个过程称作 resolve(解析),如果不同则继续用下一个 IRK 做上面的过程,直到找到一个关联 IRK 或者一个也没找到。
  3. 以T_GAP(private_addr_int) 为周期,定时更新。哪怕在广播、扫描、已连接等过程中,也可能改变。
  4. Resolvable Private Address 不能单独使用,因此需要使用该类型的地址的话,设备要同时具备 Public Device Address 或者 Static Device Address 中的一种。

二、分析广播包中蓝牙MAC地址

使用抓包工具抓取类似如下数据包:

其中数据包第 6 部分:

其中 TxAdd 表示发送方的地址类型(0 为 public,1为 random)。
RxAdd 表示接收方的地址类型。
对于普通广播来说,只有 TxAdd 的指示是有效的,表示广播发送者的第一类型。而对于定向广播来说,TxAdd 和 RxAdd 都是有效的。

其中数据包第 7 部分:

如果是随机设备地址,则查看地址的最高两位。

  • 如果是 “11” 就是静态随机地址。
  • 如果是 “00” 就是不可解析私密地址。
  • 如果是 “01” 就是可解析私密地址,并执行上面说过的 ah 方法进行解析。

三、读取MAC地址

  1. 包含头文件
#include "ble_gap.h"
1
  1. 定义结构体变量
ble_gap_addr_t bleAddr;
1

BLE 的地址由 ble_gap_addr_t 进行管理:

/**@brief Bluetooth Low Energy address. */
typedef struct
{uint8_t addr_id_peer : 1;       /**< Only valid for peer addresses.This bit is set by the SoftDevice to indicate whether the address has been resolved froma Resolvable Private Address (when the peer is using privacy).If set to 1, @ref addr and @ref addr_type refer to the identity address of the resolved address.This bit is ignored when a variable of type @ref ble_gap_addr_t is used as input to API functions. */uint8_t addr_type    : 7;       /**< See @ref BLE_GAP_ADDR_TYPES. */uint8_t addr[BLE_GAP_ADDR_LEN]; /**< 48-bit address, LSB format.@ref addr is not used if @ref addr_type is @ref BLE_GAP_ADDR_TYPE_ANONYMOUS. */
} ble_gap_addr_t;
12345678910111213
  • addr_id_peer:仅对匹配地址有效。此位由软件设置,以指示该地址是否已从可解析私密地址中解析(当匹配加密时)
  • addr_type:地址类型,作为传入参数时可忽略
  • addr:48-bit 的MAC地址数组,低字节在前,所以与我们实际看到的地址顺序相反
  1. 调用获取地址函数
    uint32_t sd_ble_gap_addr_get(ble_gap_addr_t *p_addr)
sd_ble_gap_addr_get(&bleAddr);
1
  1. 将地址逆序并打印出来
#include "nrf_log.h"uint8 address[6];
uint8 i;
for(i = 0; i < 6; i++)
{address[i] = bleAddr.addr[5 - i];    // 逆序
}NRF_LOG_INFO("address:%02x", address[0]);
NRF_LOG_INFO("address:%02x", address[1]);
NRF_LOG_INFO("address:%02x", address[2]);
NRF_LOG_INFO("address:%02x", address[3]);
NRF_LOG_INFO("address:%02x", address[4]);
NRF_LOG_INFO("address:%02x", address[5]);
123456789101112131415

这样打印出来的地址就跟我们实际扫描到的地址顺序相同。

四、修改MAC地址

首先采用 sd_ble_gap_addr_get 读取官方默认的 MAC 地址,然后再默认地址+1,再用 sd_ble_gap_addr_set 写入到设备中。

void mac_set(void)
{ble_gap_addr_t addr;uint32_t err_code = sd_ble_gap_addr_get(&addr);APP_ERROR_CHECK(err_code);// Increase the BLE address by one when advertising openly.addr.addr[0] += 1;err_code = sd_ble_gap_addr_set(&addr);APP_ERROR_CHECK(err_code);
}
123456789101112

主函数中进行调用,注意,一定要在广播开始前设置,下次广播后新的 MAC 地址就设置成功。

/**@brief Application main function.*/
int main(void)
{bool erase_bonds;// Initialize.uart_init();log_init();timers_init();buttons_leds_init(&erase_bonds);power_management_init();ble_stack_init();gap_params_init();mac_set();gatt_init();services_init();advertising_init();conn_params_init();// Start execution.advertising_start();// Enter main loop.for (;;){idle_state_handle();}
}
1234567891011121314151617181920212223242526272829

新的 MAC 地址加1,设置成功。

NRF52832学习笔记(11)——蓝牙MAC地址相关推荐

  1. nrf52832学习笔记(1)蓝牙心电例程分析

    对于我这种之前完全不懂蓝牙,接触学习nordic的蓝牙感觉学起来有困难,他那api讲解文档竟然网页版的,而且链接一层又一层,网速又慢,协议栈版本又多= .= 但还是要学啊,就拿着他的例字代码看吧... ...

  2. ESP32学习笔记(35)——蓝牙MAC地址

    一.背景 一个 BLE 设备,可以使用两种类型的地址(一个 BLE 设备可同时具备两种地址): Public Device Address(公共设备地址) Random Device Address( ...

  3. nrf52832 学习笔记(三)蓝牙从机广播

    nrf52832 学习笔记(三)蓝牙从机广播 蓝牙从机要想被主机连接,首先需要发送广播信息,周围主机通过扫描广播信号,根据从机的广播信息,判断是否连接. 蓝牙协议栈初始化 不管是主机还是从机,要想使用 ...

  4. nrf52832 学习笔记(五)蓝牙主从机连接和连接参数更新

    nrf52832 学习笔记(五)蓝牙主从机连接和连接参数更新 主机连接 nrf52832 SDK中主机连接从机需要使用 sd_ble_gap_connect(ble_gap_addr_t const ...

  5. NRF52832之蓝牙MAC地址修改

    一.背景 一个 BLE 设备,可以使用两种类型的地址(一个 BLE 设备可同时具备两种地址): Public Device Address(公共设备地址) Random Device Address( ...

  6. nrf52832 学习笔记(七)蓝牙协议层级理解

    nrf52832 学习笔记(七)蓝牙协议层级理解 本文主要由一下几篇文档摘录汇总而成 ,如有错误欢迎斧正 da14531 蓝牙协议文档 深入浅出低功耗蓝牙(BLE)协议栈 低功耗蓝牙ATT/GATT/ ...

  7. nrf52832 学习笔记(四)蓝牙主机扫描

    nrf52832 学习笔记(四)蓝牙主机扫描 从机发出广播后就需要主机进行扫描了,主机扫描之前和从机一样,也需要协议栈初始化.GAP初始化.GATT初始化,这些和从机类似,参考 nrf52832 学习 ...

  8. android 11 kotlin获取蓝牙mac地址

    android 11 kotlin获取蓝牙mac地址 加权限 要获取system.uid系统权限,具体不细述,网上可查 AndroidManifest.xml中添加蓝牙权限 <uses-perm ...

  9. nrf52832 学习笔记(九)蓝牙主机发现服务

    nrf52832 学习笔记(九)蓝牙主机发现服务 服务发现流程 数据如同下表一样存储在服务端,客户端首先要获取表中的Handle和Type列,从而知道服务端中存在哪些数据,以便后面读.写.通知等操作. ...

最新文章

  1. Remove Nth Node From End of List leetcode java
  2. python 表格格式输出_利用python对excel中一列的时间数据更改格式操作
  3. Eclipse Android插件中的“调试证书已过期”错误
  4. Python selenium 滑块验证--分析与实现
  5. 接口练习题(书信接口)
  6. 机器人也是“艺术家”!上海世界移动大会聚焦5G时代
  7. Head First JSP---随笔九(Web应用安全)
  8. C++学习笔记(11) 重载流插入运算符和流提取运算符,以及自动类型转换
  9. java oracle时间格式转换_oracle时间格式转换问题 ORA-01810: format code appears twice--转...
  10. 导出微信添加的自定义表情(动图)
  11. 什么是电容器与电容?
  12. 4.4.1 External and Internal Ratings
  13. 20140309_博瑞学习
  14. IPv6系列-彻底弄明白有状态与无状态配置IPv6地址
  15. linux文件权限管理实验心得,Linux+文件权限管理实验
  16. CANoe隐藏属性——Multi CANoe
  17. 万字长文:盘点2022全球10大数据泄漏事件(红蓝攻防角度)
  18. NLP系列 2.特征提取
  19. h5物体拖动_【点我解锁】11种网易爆款H5交互手势及案例
  20. 电信网络电视显示服务器出错,电信网络电视错误代码0025是什么问题

热门文章

  1. unity如何调用另一个脚本中的变量
  2. 需要账号密码验证的代理ip使用
  3. PCB板上的白色残留物怎么来的呢?
  4. 谷歌浏览器怎么打开开发者模式 谷歌浏览器打开开发者模式的方法
  5. 【狂神说】Docker 学习笔记【基础篇】
  6. 华为修改优先级命令_华为LTE重选参数命令简介
  7. 苹果id登录_英雄联盟手游用苹果id登录显示账号异常的解决方法_英雄联盟手游...
  8. Android Studio的res自动生成的文件出错了_莫韵乐与bug的奇妙冒险
  9. java 获取当前是周几_java 获取今天(某一天)是星期几/周几
  10. 用Python画小猪佩奇