你有没有想过:手机的蓝牙地址为什么每次扫描看到的都不一样?为什么 iPhone 明明是同一台设备,在 nRF Connect 里却隔一段时间就换个"身份"?这背后就是 BLE 的设备地址 —— BD_ADDR 在起作用。这篇文章把 BD_ADDR 的来龙去脉、地址类型、隐私机制彻底讲清楚。
BD_ADDR 全称 Bluetooth Device Address,是蓝牙设备的物理地址,长度 48 位(6 字节),作用相当于以太网世界里的 MAC 地址 —— 用来在链路层唯一标识一台设备。
在空口(over the air)和大多数协议栈 API 里,它按 小端序(little-endian) 传输和存储:字节顺序是反的。比如你在手机上看到显示为:
AA:BB:CC:DD:EE:FF
在抓包工具(如 Wireshark / nRF Sniffer)的原始字节里,它实际是:
FF EE DD CC BB AA
这个字节序问题是新手抓包时最常见的困惑来源之一,务必记住"显示是大端、空口是小端"。
BLE 地址不是只有一种。链路层的广播包和连接请求里,除了 48 位地址值,还有一个 TxAdd / RxAdd 标志位,用来区分这是"公有地址"还是"随机地址"。所以判断一个地址属于哪类,光看地址值不够,必须结合这个标志位。
随机地址不需要向 IEEE 花钱申请,靠地址最高两个 bit(MSB)再细分成三种。判断规则看地址最高字节的 bit7、bit6:
| 最高 2 bit | 类型 | 说明 |
|---|---|---|
11 |
Static Random(静态随机) | 上电时确定,一个上电周期内不变 |
00 |
Non-Resolvable Private(不可解析私有) | 纯随机,不可被反推身份 |
10 |
Resolvable Private(可解析私有,RPA) | 隐私核心,可被授权方解析 |
11,其余 46 bit 随机(不能全 0 也不能全 1)。00,其余随机。这是理解"手机蓝牙地址一直在变"的核心。
10。TGAP(private_addr_int) 定义)自动更换一次。RPA 的结构(48 bit 拆成两半):
| prand (24 bit) | hash (24 bit) |
高 24 位 低 24 位
prand:随机数,最高 2 bit 固定 10。hash = ah(IRK, prand):用设备的 IRK(Identity Resolving Key,身份解析密钥) 对 prand 做一个加密函数 ah(基于 AES-128)算出来的值。生成:设备用自己的 IRK + 新随机数 prand 算出 hash,拼成新 RPA 广播出去。
解析:接收方(曾绑定过、手里存有对方 IRK)把收到的 prand 用自己保存的 IRK 重新算一遍 hash,若结果和收到的 hash 一致 → 确认"就是这台设备"。手机里存了多个已配对设备的 IRK,就逐个尝试解析(这就是所谓的 resolving list)。
IRK 是在配对(bonding)阶段作为身份信息交换的。所以只有绑定过的双方才能互相解析对方的 RPA —— 这正是隐私与可用性的平衡点。
早期蓝牙用固定 Public 地址,带来严重的隐私追踪问题:商场、地铁只要部署一堆蓝牙扫描器,就能凭固定地址记录"某台设备(某个人)几点出现在哪里"。
RPA 机制让设备对陌生人呈现为不断变化的随机地址,杜绝了被动追踪;同时通过 IRK 让"自己人"仍能认出彼此。这就是为什么:
绑定之后,协议栈内部会用一个稳定的 Identity Address(身份地址) 来标识对端,它可以是对方的 Public 地址,或对方的 Static Random 地址。RPA 只是"对外的马甲",Identity Address 才是"内部档案里的真名"。开发时若要长期记住一台设备,应存它的 Identity Address,而不是随时会变的 RPA。
手机 App(nRF Connect / LightBlue):扫描列表里每个设备旁通常标注了地址类型(Public / Random)。看到地址频繁变化的,多半是 RPA。
代码里判断类型(伪代码,以最高字节 addr[5](大端显示的第一字节)为例):
// addr[] 按空口小端存储,addr[5] 是显示时的最高字节
uint8_t msb = addr[5];
uint8_t top2 = msb >> 6; // 取最高 2 bit
if (is_public) {
// TxAdd/RxAdd 标志位指示为 public
type = "Public";
} else {
switch (top2) {
case 0b11: type = "Static Random"; break;
case 0b00: type = "Non-Resolvable Private"; break;
case 0b10: type = "Resolvable Private (RPA)"; break;
default: type = "Reserved/Invalid"; break;
}
}
抓包:nRF Sniffer + Wireshark 里,btle 层能直接解出 Advertising Address 和其类型;注意原始字节是小端。
BD_ADDR 是 BLE 设备的 48 位"身份证",分 Public(IEEE 分配、永久唯一)和 Random(Static / Non-Resolvable / Resolvable)两大类。其中 RPA(可解析私有地址) 用 IRK 在"对陌生人隐身"和"对自己人可认"之间取得平衡,是现代蓝牙隐私保护的基石 —— 这就是你手机蓝牙地址总在变、耳机却仍能自动回连的原因。