Mesh 传输层与安全Mesh 协议栈层次(完整)Mesh 协议栈从下到上分为 6 层,Network 网络层之上就是 Lower Transport Layer(下层传输层),然后是 Upper Transport。┌─────────────────────────────────────┐ │ Models / Foundation Models │ ← 模型层(业务:OnOff、Lightness 等) ├─────────────────────────────────────┤ │ Access Layer │ ← 接入层(Opcode 解析、Model 路由) ├─────────────────────────────────────┤ │ Upper Transport Layer │ ← 上层传输(AppKey/DevKey 加密、TransMIC) ├─────────────────────────────────────┤ │ Lower Transport Layer │ ← 下层传输(分段重组、Segment ACK) ├─────────────────────────────────────┤ │ Network Layer │ ← 网络层(NetKey 加密、IV Index、Relay) ├─────────────────────────────────────┤ │ Bearer Layer │ ← 承载层(ADV Bearer / GATT Bearer) └─────────────────────────────────────┘各层职责对照层职责加密 Key加密算法BearerADV/GATT 承载无无(只封装 AD Structure)Network寻址、中继、IV IndexNetKey 派生的 EncKey/PrivKeyAES-CCM + XOR 混淆Lower Transport分段重组、Segment ACK无(不加密,只切包)无Upper TransportAccess PDU 加密、TransMICAppKey 或 DevKeyAES-CCMAccessOpcode 解析、Model 路由无无Models业务逻辑(OnOff 等)无无Network 层与传输层的关系Network PDU 结构: ┌──────────┬──────────┬────────┬─────────┬─────────┬───────────┬──────┐ │ IVI/NID │ CTL/TTL │ SEQ │ SRC │ DST │ Transport │NetMIC│ │ │ │ │ │ │ PDU │ │ └──────────┴──────────┴────────┴─────────┴─────────┴───────────┴──────┘ ↑ 这部分就是传输层产生的Network 层的Transport PDU字段就是传输层产生的——传输层负责把上层消息切成段、加密、加 TransMIC,然后交给 Network 层套上网络头(IVI/NID/CTL/TTL/SEQ/SRC/DST)再加密一遍。双重加密结构(关键)应用消息从上到下经过两层加密:┌─────────────────────────────────────────────────────────────┐ │ Access PDU(明文:Opcode + 参数) │ │ ↓ Upper Transport 加密 │ ├─────────────────────────────────────────────────────────────┤ │ 加密的 Access PDU + TransMIC(用 AppKey/DevKey + AppNonce) │ │ ↓ Lower Transport(可能分段) │ ├─────────────────────────────────────────────────────────────┤ │ Transport PDU(分段后的密文 + TransMIC) │ │ ↓ Network 层加密 │ ├─────────────────────────────────────────────────────────────┤ │ 加密的 Transport PDU + NetMIC(用 EncKey + NetworkNonce) │ │ ↓ 封装到 AD Structure │ ├─────────────────────────────────────────────────────────────┤ │ 0x2A Network PDU 空口发送 │ └─────────────────────────────────────────────────────────────┘两层加密各自独立:Upper Transport 加密:用AppKey/DevKey+ Application Nonce,加密 Access PDU,生成TransMICNetwork 加密:用Encryption Key(NetKey 派生)+ Network Nonce,加密 Transport PDU,生成NetMIC接收方反向解两层:Network 层解密 → 拿到 Transport PDULower Transport 重组 → 拿到完整 Access PDU + TransMICUpper Transport 解密 + 验 TransMIC → 拿到明文 Opcode + 参数两层 MIC 的区别(容易混淆)MIC由谁生成用什么 Key长度验证什么NetMICNetwork 层Encryption Key(NetKey 派生)4B(CTL=0) / 8B(CTL=1)网络层完整性(DST + Transport PDU)TransMICUpper Transport 层AppKey / DevKey4B(SZMIC=0) / 8B(SZMIC=1)应用层完整性(Access PDU)注意:NetMIC 和 TransMIC 是两个独立的 MIC,分别由不同层用不同 Key 生成。TransMIC 和 NetMIC 的位置(都在尾部!)两个 MIC 都在各自加密区的尾部,不在头部。这是 AES-CCM 的标准结构Ciphertext || MIC。空口 Network PDU 完整布局: ┌──────────┬──────────┬────────┬─────────┬─────────┬─────────────────────┬──────┐ │ IVI/NID │ CTL/TTL │ SEQ │ SRC │ DST │ Transport PDU │NetMIC│ │ 1B │ 1B │ 3B │ 2B │ 2B │ (变长) │ 4/8B │ │ 明文 │ 混淆 │ 混淆 │ 混淆 │ 加密 │ 加密 │ 尾部 │ └──────────┴──────────┴────────┴─────────┴─────────┴─────────────────────┴──────┘ ↑ Transport PDU 展开: Transport PDU: ┌─────────────────────┬──────────────────────────────┬──────────┐ │ Lower Trans 头 │ Access PDU 密文 │ TransMIC │ │ [SEG][AKF][AID] │ [加密的 Opcode + 参数] │ 4B/8B │ │ 1B │ │ 尾部! │ └─────────────────────┴──────────────────────────────┴──────────┘ 明文(Network 解密后可见) Upper Transport 加密 Upper Transport 生成MIC位置由谁生成加密什么生成TransMICAccess PDU尾部Upper Transport 层AppKey/DevKey + AppNonce 加密 Access PDUNetMICNetwork PDU尾部(最外层)Network 层EncKey + NetworkNonce 加密 DST+TransportPDU从外到内的顺序:[Network 头] [Lower Trans 头] [Access PDU 密文] [TransMIC] [NetMIC] ↑ ↑ ↑ Upper Transport 加密区 Network 加密区尾部接收方解密顺序(从外到内):Network 层解密 → 验 NetMIC(尾部)→ 去掉 NetMIC → 拿到 Transport PDULower Transport 重组(如分段)Upper Transport 解密 → 验 TransMIC(尾部)→ 去掉 TransMIC → 拿到明文 Opcode + 参数传输层要学的核心知识点主题关键点Lower Transport分段重组、Segment ACK、SEQ Auth、Segmented Access/ControlUpper TransportAppKey/DevKey 加密、Application Nonce、TransMICAppKey应用密钥,从 NetKey 派生(k1),绑定到 ModelDevKey设备密钥,配网时 ECDH 派生,每节点唯一,Config 消息专用TransMIC传输层 MIC,4B(Access)或 8B(Control)分段消息长消息切 12B/段,每段单独走 Network PDUFriend 缓存Friend 帮 LPN 缓存分段消息,LPN Poll 时取传输层传输层分为两层:Lower Transport Layer(下层传输)和Upper Transport Layer(上层传输)。Lower Transport Layer(下层传输)核心职责:分段重组分段原因Network PDU 最大 29 字节(ADV Bearer)或 31 字节(GATT Bearer)Access Payload 可能远大于此需要分段发送,接收端重组Access 消息和 Control 消息的 Lower Transport 格式不同重要:Lower Transport 头部格式对 Access 和 Control 消息是不同的!源码transport.h/transport.c:/* Access 第 1 字节 */#defineUNSEG_HDR(akf,aid)((akf6)|(aidAID_MASK))/* SEG=0 */#defineSEG_HDR(akf,aid)(UNSEG_HDR(akf,aid)|0x80)/* SEG=1 *//* Control 第 1 字节 */#defineTRANS_CTL_HDR(op,seg)((opTRANS_CTL_OP_MASK)|(seg7))消息类型第 1 字节格式Access 未分段[SEG=0][AKF(1b)][AID(6b)]Access 分段[SEG=1][AKF(1b)][AID(6b)]Control 未分段[SEG=0][Opcode(7b)]Control 分段[SEG=1][Opcode(7b)]Access 消息的 Lower Transport 格式未分段(SEG=0):┌─────────────────────────┬──────────────────────────┬──────────┐ │ 第1字节 │ Access PDU 密文 │ TransMIC │ │ [SEG=0][AKF][AID] │ [Opcode + 参数] │ 4B/8B │ │ 1B │ │ 尾部 │ └─────────────────────────┴──────────────────────────┴──────────┘分段(SEG=1):┌─────────────────────────┬─────────────────────────────────┬──────────────┐ │ 第1字节 │ 第2~4字节(分段头) │ 分段数据 │ │ [SEG=1][AKF][AID] │ [SZMIC][SeqZero(13b)][SegO][SegN]│ 最大 12B │ │ 1B │ 3B │ │ └─────────────────────────┴─────────────────────────────────┴──────────────┘ 分段头第 2~4 字节展开: ┌───────┬─────────────────────┬─────────┬─────────┐ │ SZMIC │ SeqZero (13 bit) │ SegO │ SegN │ │ 1b │ │ 5b │ 5b │ └───────┴─────────────────────┴─────────┴─────────┘ bit7 bit6~18 bit19~23 bit24~28 SZMIC = 0: TransMIC 32-bit (4B) SZMIC = 1: TransMIC 64-bit (8B) SeqZero = SEQ 的低 13 位(用于标识一组分段) SegO = 当前段序号 (0~31) SegN = 总段数 - 1 (最大 31,即 32 段)分段示例(3 段):Segment 0: [SEG=1][AKF][AID] [SZMIC][SeqZero][SegO=0][SegN=2] [Data 0~12B] Segment 1: [SEG=1][AKF][AID] [SZMIC][SeqZero][SegO=1][SegN=2] [Data 0~12B] Segment 2: [SEG=1][AKF][AID] [SZMIC][SeqZero][SegO=2][SegN=2] [Data 0~12B]每段单独封装成一个 Network PDU,各自有自己的 SEQ(递增)。Control 消息的 Lower Transport 格式未分段(SEG=0):┌─────────────────────────┬──────────────────┐ │ 第1字节 │ Control PDU 载荷 │ │ [SEG=0][Opcode(7b)] │ [参数] │ │ 1B │ │ └─────────────────────────┴──────────────────┘ ↑ 没有 TransMIC!分段(SEG=1):┌─────────────────────────┬─────────────────────────────────┬──────────────┐ │ 第1字节 │ 第2~4字节(分段头) │ 分段数据 │ │ [SEG=1][Opcode(7b)] │ [RFU][SeqZero(13b)][SegO][SegN] │ 最大 8B │ │ 1B │ 3B │ │ └─────────────────────────┴─────────────────────────────────┴──────────────┘ 分段头第 2~4 字节展开: ┌───────┬─────────────────────┬─────────┬─────────┐ │ RFU │ SeqZero (13 bit) │ SegO │ SegN │ │ 1b │ │ 5b │ 5b │ └───────┴─────────────────────┴─────────┴─────────┘ bit7 bit6~18 bit19~23 bit24~28 RFU = 保留(Control 没有 TransMIC,不需要 SZMIC)Access vs Control 分段格式对比维度Access 消息Control 消息第 1 字节[SEG][AKF][AID][SEG][Opcode]分段头 bit7SZMIC(TransMIC 长度)RFU(保留)每段最大数据12 字节8 字节TransMIC有(4B/8B)无Opcode 位置Access PDU 内(解密后可见)第 1 字节低 7 bit(明文)分段数据最大长度源码确认:/* access.h */#defineBT_MESH_APP_SEG_SDU_MAX12/* Access 每段最大 12 字节 *//* transport.h */#defineBT_MESH_CTL_SEG_SDU_MAX8/* Control 每段最大 8 字节 */消息类型每段最大数据最大段数最大总长Access12 字节32 段12 × 32 =384 字节Control8 字节32 段8 × 32 =256 字节分段数据是什么的切片分段数据是 Upper Transport 加密后的 (Access PDU 密文 + TransMIC) 的切片。源码transport.c的seg_tx_buf_build:staticvoidseg_tx_buf_build(structseg_tx*tx,uint8_tseg_o,structos_mbuf*buf){uint8_tlen=MIN(seg_len(tx-ctl),tx-len-(seg_len(tx-ctl)*seg_o));...net_buf_simple_add_mem(buf,tx-seg[seg_o],len);/* 分段数据 */}tx-seg[seg_o]就是 Upper Transport 加密后的 (密文 + TransMIC) 的切片。完整流程Upper Transport 加密: Access PDU 明文 [Opcode + 参数] → AES-CCM(AppKey/DevKey, AppNonce) → [Access PDU 密文 + TransMIC] ← 这是待分片的整体 Lower Transport 分片: [Access PDU 密文 + TransMIC] 整体 → 切成 12B 一段(Access)或 8B 一段(Control) → 每段加上 Lower Transport 头 每个分段的 Network PDU: [Network 头][Lower Trans 头][分段数据(密文+TransMIC 的切片)][NetMIC]关键:TransMIC 也被切进分段数据里,不是单独的字段。接收方收齐所有段拼起来,才能拿到完整的 TransMIC 做校验。切片示例示例:Access PDU 密文(20B) + TransMIC(4B) = 24B,分 2 段(每段 12B): Segment 0: [Lower Trans 头] [密文 0~11B] ← 12B 切片 Segment 1: [Lower Trans 头] [密文 12~19B + TransMIC 4B] ← 12B 切片 接收方拼起来: [密文 0~19B + TransMIC 4B] = 24B → 用 AppKey/DevKey 解密 + 验 TransMICSeqZero 的作用SeqZero = SEQ 的低 13 位 发送一组分段消息时: Segment 0: SEQ=100, SeqZero=100 0x1FFF Segment 1: SEQ=101, SeqZero=100 0x1FFF ← 和 Segment 0 相同! Segment 2: SEQ=102, SeqZero=100 0x1FFF ← 和 Segment 0 相同! 所有段共享同一个 SeqZero,接收方用 SeqZero 判断「这些段属于同一组消息」SeqZero 是重组的关键标识——接收方用 (SRC, SeqZero) 唯一标识一组分段消息,把收到的段按 SegO 顺序拼起来。分段确认(Segment ACK)重要:接收方每收到一段就发 ACK,不是等全部到齐才发。这样发送方能及时知道哪些段丢了,针对性重传。源码transport.c在多处调用send_ack:/* 收到段时(部分到齐也发 ACK) */send_ack(rx-sub,rx-dst,rx-src,rx-ttl,rx-seq_auth,rx-block,rx-obo);/* 全部到齐时也发 ACK */send_ack(net_rx-sub,net_rx-ctx.recv_dst,net_rx-ctx.addr,...);ACK 消息格式源码send_ack:uint8_tbuf[6];sys_put_be16(((seq_zero2)0x7ffc)|(obo15),buf);/* 2 字节 */sys_put_be32(block,buf[2]);/* 4 字节 */bt_mesh_ctl_send(tx,TRANS_CTL_OP_ACK,buf,sizeof(buf),...)