3. SecOC PDU格式详解:头部结构、数据载荷与填充机制
好,咱们今天来啃一块硬骨头——SecOC PDU的格式。说实话,我在刚接触SecOC那会儿,看着协议栈里那一串十六进制数据,头都是大的。但后来我发现,只要把PDU拆成三块来看,其实没那么复杂。
SecOC PDU说白了就三个部分:头部(Header)、数据载荷(Payload)和填充(Padding)。咱们一个一个说。
3.1 SecOC PDU头部结构
头部是SecOC的灵魂。它包含两个关键字段:Freshness Value(新鲜度值)和Auth Tag(认证标签)。我习惯把Freshness Value比作“身份证有效期”,把Auth Tag比作“防伪码”。
3.1.1 Freshness Value(新鲜度值)
Freshness Value的作用是什么?防止重放攻击。你想想看,如果攻击者截获了一条合法的CAN报文,然后不断重发,ECU怎么知道这是不是过期的消息?Freshness Value就是干这个的。
它的结构通常包含两部分:
- 时间戳(Timestamp):比如从某个基准时间开始的毫秒数
- 计数器(Counter):比如一个单调递增的序列号
在实际项目中,我见过两种主流配置:
| 配置类型 | 长度 | 典型场景 |
|---|---|---|
| 纯计数器 | 4字节 | 低成本ECU,无RTC硬件 |
| 时间戳+计数器 | 8字节 | 网关、域控制器 |
3.1.2 Auth Tag(认证标签)
Auth Tag是SecOC的“签名”。它通过MAC算法(比如CMAC、HMAC)对PDU的其余部分计算得出。接收方收到后,用同样的密钥和算法重新计算,比对是否一致。
Auth Tag的长度取决于安全等级:
- 4字节:最低安全要求,常用于CAN FD
- 8字节:推荐配置,平衡安全性和带宽
- 16字节:最高安全等级,用于关键控制消息
3.2 数据载荷(Payload)
数据载荷就是原始的应用数据。比如一个车窗控制指令、一个车速信号。SecOC不对载荷内容做任何修改,它只负责“包装”。
这里有个关键点:载荷长度必须是固定的。为什么?因为接收方需要知道从哪里开始解析Auth Tag。如果载荷长度可变,那解析逻辑就复杂了。
嗯,这里要注意:有些协议栈允许载荷长度变化,但必须在PDU头部显式声明长度。我个人不建议这么做——增加复杂度,容易出bug。
3.3 填充机制(Padding)
填充机制是SecOC里最容易被忽视的部分。它的作用有两个:
- 对齐:让PDU总长度符合底层通信协议的要求(比如CAN FD的64字节对齐)
- 防长度分析:防止攻击者通过PDU长度推断消息类型
填充的规则其实很简单:
- 填充值通常用
0x00或0xFF - 填充位置在Auth Tag之后
- 接收方收到后,先去掉填充,再验证Auth Tag
3.4 完整的SecOC PDU结构示例
咱们来看一个实际的例子。假设我们要发送一条车窗控制消息:
原始载荷: 0x01 0x02 0x03 0x04 (4字节)
Freshness Value: 0x00000001 (4字节计数器)
Auth Tag: 0xA1B2C3D4 (4字节)
填充: 0x00 0x00 (2字节,对齐到10字节)
完整的SecOC PDU:
0x00 0x00 0x00 0x01 | Freshness Value (4字节)
0x01 0x02 0x03 0x04 | 数据载荷 (4字节)
0xA1 0xB2 0xC3 0xD4 | Auth Tag (4字节)
0x00 0x00 | 填充 (2字节)
你看,接收方收到后,先根据协议约定知道Freshness Value是4字节,载荷是4字节,Auth Tag是4字节,剩下的就是填充。去掉填充后,用同样的密钥计算Auth Tag,比对一致,就说明消息是合法的。
3.5 总结与建议
SecOC PDU的设计,说白了就是“在有限带宽里做最安全的事”。我个人建议:
- Freshness Value:能用8字节就别用4字节,除非你的计数器永远不会回绕
- Auth Tag:8字节是黄金配置,别盲目追求16字节
- 填充:设计阶段就定好填充值和规则,别留到集成阶段再扯皮
我记得有一次评审,一个同事问:“为什么SecOC不直接用TLS?” 我笑了笑说:“你想想看,CAN总线上跑TLS,光握手就要几秒钟,车都撞上了还在握手呢。” 所以,SecOC的设计哲学就是——轻量、高效、够用就好。
下一章咱们聊聊SecOC的密钥管理,那又是一个大坑。嗯,到时候我给你们讲讲我在密钥分发上踩过的雷。