配网协议设计:NDEF数据格式、自定义配网协议字段、安全校验机制
好,咱们接着聊配网协议。上一章我们把NFC碰触的物理层和基础流程讲清楚了,这一章要深入核心——数据到底怎么装、怎么传、怎么保证安全。
说实话,配网协议设计是整个方案里最容易踩坑的地方。我见过太多团队,硬件调通了,NFC也能读了,结果配网成功率不到60%。为什么?协议设计不合理。数据格式乱、字段冲突、没有校验,一碰就死机。嗯,咱们今天就把这些坑一个个填上。
NDEF数据格式:NFC世界的通用语言
NDEF,全称NFC Data Exchange Format。说白了,就是NFC设备之间交换数据的标准格式。你想想看,手机和Wi-Fi模组之间要对话,总得有个双方都懂的语法吧?NDEF就是这个语法。
一个NDEF消息由若干条记录组成。每条记录包含三个部分:
- 头部:记录类型、长度、是否最后一条等元信息
- 类型:比如文本类型(T)、URI类型(U)、MIME类型
- 载荷:实际的数据内容
我个人的习惯是,配网场景只用一条NDEF记录就够了。为什么?因为NDEF记录多了,手机端的解析逻辑会变复杂,而且容易出错。一条记录,一个自定义类型,干净利落。
核心原则:NDEF记录越少越好,类型越明确越好。
举个例子,咱们定义一个自定义NDEF类型,类型名就叫application/vnd.wifi-config。手机读到这个类型,就知道这是配网数据,直接走配网解析流程。
// NDEF记录结构示例
NDEF Record {
TNF = 0x02 // Media-type as defined in RFC 2046
Type = "application/vnd.wifi-config"
ID = "" // 不需要ID字段
Payload = [自定义配网协议字段]
}
这里要注意,TNF(Type Name Format)字段要选对。我建议用0x02,表示MIME媒体类型。这样兼容性最好,iPhone和Android都能认。
自定义配网协议字段:把Wi-Fi信息装进NFC
NDEF只是容器,真正干活的是里面的自定义字段。咱们得设计一套紧凑、高效、可扩展的协议。
先说说我踩过的坑。有一次,我把SSID、密码、加密方式直接拼成JSON字符串塞进NDEF。结果呢?有些低端手机解析JSON时内存溢出,直接崩溃。从那以后,我改用二进制TLV格式。
TLV,就是Type-Length-Value。每个字段由三部分组成:
- Type(1字节):字段类型标识
- Length(1-2字节):字段值的长度
- Value(变长):字段的实际数据
咱们定义以下字段类型:
| Type值 | 字段名称 | 长度 | 说明 |
|---|---|---|---|
| 0x01 | SSID | 1-32字节 | Wi-Fi名称,UTF-8编码 |
| 0x02 | 密码 | 8-64字节 | Wi-Fi密码,UTF-8编码 |
| 0x03 | 加密方式 | 1字节 | 0x00=无, 0x01=WPA, 0x02=WPA2, 0x03=WPA3 |
| 0x04 | 频段 | 1字节 | 0x00=2.4G, 0x01=5G, 0x02=双频 |
| 0x05 | 设备ID | 6字节 | MAC地址或唯一标识 |
| 0x06 | 校验码 | 4字节 | CRC32校验 |
| 0xFF | 结束标记 | 0字节 | 表示字段结束 |
你看,每个字段都很紧凑。SSID最长32字节,密码最长64字节,整个NDEF载荷一般不超过200字节。NFC标签的存储空间通常是512字节或1KB,完全够用。
// 配网数据TLV编码示例(十六进制)
01 0C 48 65 6C 6C 6F 2D 57 69 46 69 5F 32 34 // SSID: "Hello-WiFi_24"
02 10 31 32 33 34 35 36 37 38 39 30 61 62 63 64 65 66 // 密码: "1234567890abcdef"
03 01 02 // 加密方式: WPA2
04 01 00 // 频段: 2.4G
05 06 AA BB CC DD EE FF // 设备ID: AA:BB:CC:DD:EE:FF
06 04 12 34 56 78 // CRC32校验码
FF // 结束标记
小技巧:字段顺序可以固定,也可以不固定。我建议固定顺序,这样解析代码更简单,出错率更低。
安全校验机制:别让配网变成"裸奔"
安全这块,很多人觉得NFC碰触配网是物理接触,天然安全。其实不然。你想想看,如果有人拿着一个恶意NFC标签靠近你的设备,会发生什么?
我曾经在客户现场遇到过一个问题:一个智能门锁,NFC碰触配网后,Wi-Fi密码被邻居的手机读走了。为什么?因为NFC标签里的数据是明文存储的,任何支持NFC的手机都能读。
所以,安全校验机制必须做。我总结了三个层次:
第一层:数据完整性校验
用CRC32或CRC16对整个载荷做校验。设备收到数据后,重新计算CRC,和字段里的CRC比对。不一致就直接丢弃,不处理。
// CRC32校验伪代码
uint32_t calc_crc32(uint8_t *data, uint32_t len) {
// 标准CRC32算法
// 注意:计算范围不包括CRC字段本身
}
bool verify_payload(uint8_t *payload, uint32_t len) {
uint32_t received_crc = extract_crc(payload);
uint32_t calculated_crc = calc_crc32(payload, len - 4);
return received_crc == calculated_crc;
}
第二层:设备身份验证
设备ID字段不能只是MAC地址,容易被伪造。我建议用设备出厂时烧录的唯一密钥,对设备ID做HMAC签名。手机端读到的设备ID是签名后的,设备端用本地密钥验证签名。
注意:密钥必须在生产时安全烧录,不能通过NFC传输。否则等于把钥匙交给别人。
第三层:时效性校验
NFC标签里的数据如果被复制,可以反复使用。怎么办?加入时间戳或随机数。设备记录每次配网的时间戳,如果发现时间戳重复或过期,就拒绝配网。
具体做法:在TLV字段里加一个0x07类型,存放Unix时间戳(4字节)。设备端维护一个最近10分钟的时间戳列表,新来的时间戳如果在列表里,直接丢弃。
// 时间戳防重放伪代码
#define TIMESTAMP_WINDOW 600 // 10分钟
bool check_timestamp(uint32_t ts) {
uint32_t now = get_unix_time();
if (abs(now - ts) > TIMESTAMP_WINDOW) {
return false; // 时间戳过期
}
if (timestamp_seen(ts)) {
return false; // 时间戳已使用
}
record_timestamp(ts);
return true;
}
总结一下:配网协议设计,NDEF格式是骨架,自定义字段是血肉,安全校验是灵魂。三者缺一不可。
嗯,这一章的内容就到这里。下一章咱们聊聊手机端的SDK集成和开发注意事项。到时候我会分享一些Android和iOS上的坑,都是真金白银换来的经验。