配网协议设计: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上的坑,都是真金白银换来的经验。