Smart Config 技术原理
一次偶然见识了一台无线监控设备的网络接入方式,从而了解到了 SmartConfig 技术。
本文在于阐述我对 SmartConfig 这一技术的理解。
简述
简单来说,这是一种让你可以在没有和其他设备(支持 SmartConfig 技术)建立任何性质的通讯链路的情况下,配置该设备接入 wifi 网络。
虚构一个实际场景的话,会是这样:
你购买了一个带有 wifi 的摄像头,不过这个摄像头没有 USB、没有以太网、没有蓝牙、没有 NFC,GSM 就更不可能了,只有 wifi,那么问题来了:
你如何配置这个摄像头接入你家的 wifi?
乍一想,没有数据链路,如何进行数据交换?
对的,SmartConfig 就是用在这种场景下的,如果这个摄像头的 wifi 支持 SmartConfig 技术,那么你只需这样几个步骤:
- 摄像头插上电源
- 安装制造商提供的手机 app(应用无需任何特殊权限,只需要手机当前是接入 wifi 的)
- 在摄像头附近打开 app,输入你家 wifi 的密码,点击确认,稍等片刻,不出意外的话,摄像头已经接入你家 wifi 了
这项技术由德州仪器提出,并且应用在自己的 CC3000 系列芯片上。不过,从原理上来说,支持混杂模式的 wifi 芯片都可以应用该技术。
猜想
首先,你可能会联想到是不是这个 app 控制了手机,让手机主动接入摄像头的 wifi 网络,然后交换数据。这确实是一个行得通的办法,但是却不实用,控制操作系统更换当前的网络连接是敏感操作,普通的 app 没这个权限,那么就需要用户参与其中,对于一般的用户而言,这样的流程就显得复杂且难以理解了。
这样看来,摄像头并没有和你控制的任何一个设备建立任何性质的连接。
一般来说,我们潜意识里会默认通讯都是双向的,用这个习惯来看待 SmartConfig,会觉得匪夷所思。
其实,在这种场景下,我们只需要能够把 wifi 的名称和密码告诉摄像头就行了,摄像头有没有回馈并不重要。
顺着这个思路,我们发现可以这么做,手机 app 上生成一个包含 wifi 名称和密码的二维码,然后放置在摄像头前,摄像头只要识别了二维码自然就可以接入 wifi。
但是,扫二维码的方式依赖视频信号输入,并不是通用的手段(因为现实场景中的设备并不都是摄像头),而且场景里也没有采用这种做法。
这么分析下来,传播 wifi 信息的渠道只可能是 wifi 本身了。
摄像头尚未接入 wifi,况且 wifi 通信本身也是加密的,app 并没有能力控制 wifi 的底层通讯,app 又是如何将信息成功透露给摄像头的?
共识
理解 SmartConfig 原理前,先约定两个我总结的观点,作为下文的共识。
其一
无线数据的传播形式必定是广播
至少目前我们普及应用的无线通信,物理上都是电磁波的扩散辐射,本质就是广播;即便是定向天线、红外、激光对射这类窄波束传输,也只是收窄了辐射的张角,并不能精确投递到唯一的接收点。
既然是广播,那么必然可以被监听,就像一个酒吧里有两个中国人和两个俄罗斯人,中国人和中国人说话,俄罗斯人听得到,只不过听不懂,反之亦然。
其二
任何可控的模式都可以被用于编码,用于数据交换
目前 wifi 常用的几种加密方式都存在一个特点,明文的长度和加密后的密文长度之间是线性关系。
即:密文长度 = 明文长度 + 算法相关的常量 C,也就是说,只要明文长度可控,密文的长度即是可控的。
这个特点是 SmartConfig 的核心原理。
技术原理
如果了解 802.11 的帧格式,你就知道,每一个 802.11 数据帧的边界和大小在物理层抓包时是清晰可见的——尽管 WPA/WPA2 下载荷(即 IP/UDP/应用层数据)已经被加密为密文,但帧的长度本身并未被隐藏,只要接收到 802.11 帧就可以立刻得到这个长度。
常见的两种数据帧格式:


密文长度有了,接下来我们看一下明文的结构。
普通权限的应用程序是没有能力完全控制和定义传输层及下层所有协议数据的,唯一可以完全控制的就是应用层数据,那就继续分析一下 TCP/IP 协议栈中的网络层和传输层的数据结构。
常用的网络层协议非 IPv4 莫属,IPv4 的头部绝大多数情况下都是定长的 20 字节,长度几乎完全可控。
传输层我们选择 UDP,因为 UDP 协议头部为定长的 8 字节,完全可控(选择 UDP 还有别的原因:免连接,不需要三次握手;原生支持广播;不会被 TCP 的重传/拥塞控制机制干扰发包节奏)。
这么看来,我们有能力完全控制明文的长度。
明文长度 = 20 + 8 + 应用层数据长度
密文长度也就跟着确定了:
密文长度 = 20 + 8 + 应用层数据长度 + 算法相关的常量 C
如果我需要你发出一个密文长度为 1000 字节的 802.11 帧,那么你只需要在 UDP 中塞满任意 (1000 - 20 - 8 - C) 个字节即可。
接下来,只要利用可控的密文长度定义一张编码表,就能将数据传递给任何持有这张编码表的设备。
原理说完了,依此即可实现几乎任意数据的传播,至于 SmartConfig 采用何种编码,这个则是技术的实现细节,本文不讨论。
流程示例
为了示意一下整个过程,我们简单定义一张编码表:
密文长度 => 映射释义
1234 => 起始符;连续的 3 个起始符,用于表示数据传输开始
1324 => 结束符;连续的 3 个结束符,用于表示数据传输结束
110 => 间隔符;连续的 2 个间隔符,用于表示数据符之间的间隔
1000 => 数据符;表示 ASCII 0x00
1001 => 数据符;表示 ASCII 0x01
…
1127 => 数据符;表示 ASCII 0x7F
假设我们要把字符串 “CJey”(密码)告诉摄像头,整个流程大致如下(假设常量 C 为 16):
手机 app 部分:
- 打开手机 app,在输入框中填入要发送的字符串 “CJey”,点击发送
- app 连续发送三个 UDP 广播包,填充数据为 1190 个 0x00 字节(1234 - 16 - 20 - 8 = 1190),表示传输开始
- app 发送一个 UDP 广播包,填充数据为 1023 个 0x00 字节(1067 - 16 - 20 - 8 = 1023),传输字符 C
- app 连续发送两个 UDP 广播包,填充数据为 66 个 0x00 字节(110 - 16 - 20 - 8 = 66),表示数据间隔
- app 发送一个 UDP 广播包,填充数据为 1030 个 0x00 字节(1074 - 16 - 20 - 8 = 1030),传输字符 J
- app 连续发送两个 UDP 广播包,填充数据为 66 个 0x00 字节(110 - 16 - 20 - 8 = 66),表示数据间隔
- app 发送一个 UDP 广播包,填充数据为 1057 个 0x00 字节(1101 - 16 - 20 - 8 = 1057),传输字符 e
- app 连续发送两个 UDP 广播包,填充数据为 66 个 0x00 字节(110 - 16 - 20 - 8 = 66),表示数据间隔
- app 发送一个 UDP 广播包,填充数据为 1077 个 0x00 字节(1121 - 16 - 20 - 8 = 1077),传输字符 y
- app 连续发送三个 UDP 广播包,填充数据为 1280 个 0x00 字节(1324 - 16 - 20 - 8 = 1280),表示传输结束
- 从第 1 步开始循环多次,直到超时或者摄像头成功接入 wifi 后向 app 汇报成功
摄像头部分:
- 摄像头通电,没有可用的 wifi,进入混杂模式,开始监听信号覆盖范围内的所有 wifi 数据帧
- 捕获数据帧,如果连续收到 3 个密文长度为 1234,且来自于同一个发射源 X 的数据帧,则进入下一步,否则重复本步
- 捕获发射源 X 的数据帧,持续捕获密文长度为 110 或者 1000-1127 之间的数据帧,直到捕获到连续 3 个密文长度为 1324 的数据帧
- 将上述数据帧按照编码表进行映射,由于手机 app 并非是独占网络,所以捕获到的数据可能有噪音,比如解码出来的结果可能是这样(
/表示数据间隔):ACX/J/o@e/ymmm - 如果结果无歧义,记为候选 R;否则继续重复捕获 X,将新一轮的结果与既有结果取交集,循环直到收敛出唯一的 R
- 拿候选 R 进行二次验证:再捕获一轮 X,若与 R 一致则视为接收完成;否则回到第 5 步继续收敛
- 由于捕获的数据帧头部信息中已经包含了 wifi 的 BSSID 信息,使用 “CJey” 作为密码去尝试连接相应的 wifi,成功则向 app 报告,失败则回到第 2 步重新监听
参考
SmartConfig的TI官方wiki
SmartConfig的TI官方介绍
CC3000 Smart Config - transmitting SSID and keyphrase
How does TI CC3000 wifi smart config work?