资讯动态

低功耗安全握手设计:MCU状态机与轻量级协议实战

发布时间:2026/9/13 1:40:30 来源:尧图企业网站定制
1. 低功耗与安全握手绕不开的“跷跷板”任何一个做过电池类智能设备的老工程师都会告诉你一个扎心的事实安全和省电在底层逻辑上是打架的。做安全握手要加密、要校验、要握手逻辑这些都需要CPU跑起来、射频发出去每一笔都是电流开销。而做低功耗恨不得CPU永远在睡射频永远不工作只在需要上报的那一瞬醒来发完数据立刻躺回去。我早年做一款智能穿戴设备设计方案时就踩过这个坑。产品定位是纽扣电池供电、续航要超过一年、每天向手机同步几次运动数据。当时直接在链路上跑完整的 TLS 握手——双向证书校验、密钥协商、完整握手报文——结果实测下来单次握手的平均电流峰值到了 80mA 左右耗时接近 1.2 秒。对于一块 220mAh 的纽扣电池来说这种握手频率根本撑不住一年。最后只能推翻重做换成了一整套轻量级的低功耗安全握手方案。所以说电池类设备的低功耗安全握手核心从来不是“选一个最安全的算法”而是找到安全强度、握手耗时、功耗开销三者之间的平衡点。这篇就把我这几年在 HC32F460、NXP RT1050 这类 MCU 上做低功耗设计、安全握手的一些实操经验整理出来重点讲状态机怎么设计、握手协议怎么裁剪、功耗怎么实测、坑又在哪里。2. 低功耗状态机设计光会睡没用要睡得快醒得也快2.1 先从 MCU 的低功耗模式说起低功耗安全握手的前提是设备平时必须能“睡”得很沉。目前主流的电池类智能设备MCU 低功耗状态基本分这么几档模式典型电流唤醒延迟适合场景Run全速运行几 mA 到几十 mA-数据采集、握手运算、射频发送Sleep浅睡几十 µA几 µs等待外设事件短时暂停Stop / Deep Sleep深睡几 µA 到十几 µA几十 µs 到几百 µs待机、定时唤醒、传感器低频率采样Shutdown / Standby掉电几百 nA 到几 µA几百 µs 到几 ms长期休眠、RTC 定时唤醒我用的比较多的是 HC32F460 这款国产 MCU它的低功耗表现相当能打Stop 模式能做到 3µA 左右关闭 LDO、保留 SRAM。之前还专门整理过一份 HC32F460 低功耗笔记里面关键就是进 Stop 模式之前必须把所有不用的外设时钟关干净GPIO 状态全部设成确定电平上拉或下拉不然漏电路径分分钟让整机电流多出几十微安。这个“GPIO 悬空导致漏电”的问题是我见过的低功耗项目里排名第一的坑后面问题排查部分我会细讲。NXP RT1050 这类主频跑到 500MHz 甚至更高的跨界 MCU 则不太一样它的功耗大头在核心电压和 PLL。在做低功耗设计时RT1050 通常要降到低主频模式比如 24MHz 内部 RC再进 Deep Sleep才能把功耗压低。用 RT1050 做安全握手有个好处——算力充裕可以跑更重的加密算法但代价就是主控自身的基础功耗偏大所以它更适合那种“平时用协处理器保持低功耗、需要握手时再唤醒 RT1050”的异构架构不太适合直接做纯电池供电的待机场景。2.2 idle 休眠模式的正确用法区分“无事可做”和“正在等待”很多人一提低功耗就想着直接睡死过去但在安全握手的场景里设备的“睡”至少还能细分成两种情况。一种是IDLE 模式——CPU 暂时没有任务在跑但外设还在工作比如射频模块正在等网关的应答包、传感器在做 ADC 转换。这种时候你完全可以让 CPU 进入 IDLE 休眠很多 MCU 的 WFI 指令就能实现等外设中断来唤醒。WFIWait For Interrupt的好处是唤醒延迟极低通常几个时钟周期就恢复了对实时性几乎无感知。另一种是Deep Sleep 模式——整机准备关机所有外设停掉只有 RTC 或低功耗定时器在走。这时候一旦有中断唤醒CPU 需要经过时钟稳定、外设重新初始化、电源域恢复等流程耗时可能达到几百微秒甚至毫秒级。安全握手的过程其实是一个典型的“多段等待”过程发握手请求、等响应、发认证数据、等确认。如果这几个等待阶段你都让设备处于原速 Run 状态那功耗会非常难看。正确的做法是把握手拆成多个短小的“活跃片段”每个片段之间让设备进入 IDLE 等待而不是死等。我最早做的一版就是 write 完握手请求后直接 while 循环等 SPI 接收标志实测这段时间白白浪费了十几毫秒的运行电流。后来改成发起请求 → 进 IDLE → SPI 中断唤醒 → 校验数据包 → 再回 IDLE整机平均电流直接下降了一个数量级。在电池类设备上这种“碎片化休眠”的思路比单纯选一颗低功耗 MCU 更管用。2.3 握手场景的状态机拆解与功耗预算在真正写代码之前我建议先把一次完整的安全握手拆成状态机逐个状态估算耗时和电流这样才能算出一次握手到底吃掉了多少容量。以我做的智能穿戴设备为例当时的状态机大致是这样状态动作耗时估算平均电流估算SYNC射频模块上电、锁频、同步2ms15mAHELLO发送设备ID 随机数Nonce1ms20mAWAIT_HELLO_ACK等待网关响应5msIDLE等待0.5mAAUTH计算HMAC、组装认证包3ms8mASEND_AUTH发送认证数据1ms20mAWAIT_VERIFY等待网关确认5msIDLE等待0.5mACONFIRM校验结果、握手完成0.5ms8mASLEEP关闭射频、进入Deep Sleep0.5ms1mA按照这个状态机一次完整握手的等效电荷消耗大概是[ Q (15\times2 20\times1 0.5\times5 8\times3 20\times1 0.5\times5 8\times0.5 1\times0.5) \text{ mA·ms} ]逐项加起来大约 ( 30 20 2.5 24 20 2.5 4 0.5 103.5 \text{ mA·ms} )也就是约 0.0287 mAh。如果一天做 20 次握手一年就是 ( 0.0287 \times 20 \times 365 \approx 209.5 \text{ mAh} )。这个数字对一个 400mAh 的锂电池来说已经占了相当大一块。而如果每次握手多等 10ms、多发 100 字节全年累积下来的差距会非常可观。做低功耗安全握手账面一定要算到这么细才能放心。3. 轻量级安全握手协议怎么设计砍掉哪些、保留哪些3.1 为什么不能直接跑标准 TLS标准的 TLS 握手TLS 1.2 / 1.3安全强度确实高但对电池类设备来说有几个硬伤。首先是握手轮次多。TLS 1.2 完整握手要两次 RTTTLS 1.3 虽然优化到了 1-RTT但每次 RTT 在无线链路上都可能因为重传、对端处理速度等因素被拉长。对资源受限设备来说这意味这射频模块要长时间保持在工作状态。其次是证书处理开销大。TLS 的证书链验证需要解析 X.509 证书、验证签名这涉及到 ECC 或 RSA 的大数运算。在 Cortex-M0 级别的 MCU 上跑 ECC 的签名验证动辄几百毫秒这在低功耗场景下几乎是不可接受的。另外标准 TLS 还有证书体积大、代码占 ROM 多的问题。一个最小的 mbedTLS 也要 40KB 以上的 ROM对很多 64KB Flash 的 MCU 来说太奢侈了。所以在电池类智能设备上大多数时候我们用的是类 TLS 但裁剪过的轻量级握手协议只保留对称加密 哈希校验 随机数防重放这几个核心能力。3.2 协议设计预共享密钥 随机数 HMAC我在多个项目里沉淀下来的一套思路是这样的设备出厂时预置一个唯一 ID 和一把预共享密钥 PSKPre-Shared Key网关侧有对应的数据库。握手时采用“挑战-应答”模式设备端生成随机数 ( N_d )连同设备 ID 一起发往网关。网关收到后回复自己的随机数 ( N_g )同时用 PSK 计算一个认证码 ( MAC_g \text{HMAC}(PSK, N_d | N_g | ID_d) ) 一起返回。设备端用本地 PSK 验证 ( MAC_g )确认网关确实是持有同一把密钥的对端。设备端再用 PSK 计算 ( MAC_d \text{HMAC}(PSK, N_g | N_d | ID_d) ) 发给网关网关验证后握手完成。双方用 ( \text{PTK} \text{HKDF}(PSK, N_d | N_g) ) 派生会话密钥之后的业务数据用 AES-CCM 或 AES-GCM 加密。整个过程只有两个 RTT比标准 TLS 少了一轮。而且 HMAC 运算量远小于证书签名验证在 HC32F460 这类 M4 内核上算一次 HMAC-SHA25664 字节输入大约只需要几十微秒几乎可以忽略不计。3.3 为什么随机数 HMAC 能防重放和中间人这套协议的核心安全性来自随机数Nonce和 HMAC 的组合我给初学者解释一下为什么有效。防重放如果每次握手都用一个全新的随机数 ( N_d )那么即使攻击者录下了上一次的握手报文在下一次重放时网关会因为 ( N_d ) 不匹配而拒绝认证。很多人在实现时容易犯的一个错误是随机数重复——MCU 的伪随机数发生器如果直接用 ADC 噪声或系统 tick 做种子很容易在每次上电后产生相同的随机数序列。我的习惯是用硬件 TRNG如果 MCU 有的话或者至少混合 RTC 秒数、内部温度传感器读数等多个熵源来做种子确保随机数不重复。防中间人因为 ( MAC_g ) 是用 PSK 和双方随机数一起算的中间人即使截获了通信数据也无法伪装成网关或设备——他算不出正确的 MAC。这里要注意的是HMAC 的关键在于 PSK 的保密性所以PSK 的存储不能直接明文放在 Flash 里。很多量产设备是通过读保护RDP或安全元件来保护密钥区的如果条件有限至少要把密钥做一层 XOR 混淆或者放在被 RDP 保护的 Flash 分区里。3.4 安全强度的取舍AES-128 还是 AES-256关于对称加密算法我在电池设备上一般选AES-128-CCM而不是 AES-256。原因没那么复杂AES-128 在已知最佳攻击面下仍有 128 位安全强度对智能穿戴、传感器这类设备已经足够而 AES-256 在软件实现上大概要多出 40% 的运算时间在 CCM 模式下对功耗的影响比想象中大。如果产品定位是医疗设备或金融支付终端那确实可以考虑 AES-256但普通消费类设备AES-128 是功耗与安全的最佳平衡点。CCM 模式比 GCM 好在一点CCM 只需要 AES 加密方向不需要解密方向这在软件实现上能省下不少代码和查表开销对 8 位/16 位 MCU 尤其友好。缺点是需要提前计算 ( L ) 长度字段某些协议栈里容易配错这个后面我在问题排查那一节会专门讲。4. 协议栈与工程落地从裁剪到实测4.1 无线协议栈怎么配合低功耗握手我做了这么多个电池类设备项目通信方案用过 BLE、Zigbee、2.4G 私有协议、LoRa 等。每种协议栈对低功耗握手的影响差异很大这里放一张对比表供参考协议典型峰值电流连接耗时低功耗友好度备注BLE 4.x/5.x10~20mA广播/扫描、连接间隔影响大高有成熟 sleep 模式连接事件是周期性的适合小数据量同步Zigbee30~50mA建网/入网过程较慢中休眠节点需要协调器配合适合 Mesh 组网场景2.4G 私有协议20~40mA最快可精确控制收发时序高完全自己掌控需要自己设计重传和同步策略LoRa100~150mA较慢占空比受限中接收窗口要定时唤醒覆盖远但瞬时功耗高在智能穿戴设备设计方案里我用的最多的是 BLE 和 2.4G 私有协议。BLE 的好处是手机可以直接连接生态成熟坏处是连接事件是由 master 决定的设备端不能随心所欲地睡。如果做的是自组网场景2.4G 私有协议反而在低功耗控制上更自由——你可以完全掌控收发窗口的时间片精确到单个字节的时序。不管用哪种协议握手报文本身要尽量压缩。每多 10 字节的报文在 1Mbps 空口速率下就是额外 80µs 的射频开启时间按 20mA 计算大约是 0.00044mAh 的额外消耗。报文多了可能感觉不明显但在频繁握手、大量设备的场景里积少成多的能量消耗是很可观的。4.2 实战示例HC32F460 上的握手流程代码骨架这里我给出一个在 HC32F460 上用软件实现轻量级握手的骨架代码核心思路体现的是“分片活跃 IDLE 等待”。注意这只是流程示意真实项目中你会把 SHA256/HMAC 的实现替换成 mbedTLS 或者自己移植的库。// 状态机定义 typedef enum { HANDSHAKE_IDLE 0, HANDSHAKE_SYNC, HANDSHAKE_HELLO, HANDSHAKE_WAIT_MAC, HANDSHAKE_AUTH, HANDSHAKE_WAIT_CONFIRM, HANDSHAKE_DONE, HANDSHAKE_FAIL } handshake_state_t; static handshake_state_t hs_state HANDSHAKE_IDLE; static uint8_t nonce_dev[16]; // 设备随机数 static uint8_t nonce_gw[16]; // 网关随机数 static uint8_t psk[16]; // 预共享密钥来自安全存储区 static uint8_t ptk[16]; // 会话密钥 void handshake_start(void) { // 1. 生成设备随机数 trng_generate_random(nonce_dev, sizeof(nonce_dev)); // 2. 进入状态机 hs_state HANDSHAKE_SYNC; handshake_task(); } void handshake_task(void) { switch (hs_state) { case HANDSHAKE_SYNC: // 打开射频模块电源等待锁频完成 rf_power_on(); rf_wait_pll_lock(2); // 等待PLL锁定约1-2ms hs_state HANDSHAKE_HELLO; // 主动继续执行下一步也可用事件触发 handshake_task(); break; case HANDSHAKE_HELLO: // 组包设备ID 随机数 uint8_t hello_pkt[8 16]; memcpy(hello_pkt, device_id, 8); memcpy(hello_pkt 8, nonce_dev, 16); rf_send_packet(hello_pkt, sizeof(hello_pkt)); hs_state HANDSHAKE_WAIT_MAC; // 进入IDLE等待由射频 RX 中断唤醒 mcu_enter_idle_mode(); break; case HANDSHAKE_WAIT_MAC: // 收到网关回复提取 nonce_gw 和 MAC_g // 验证 HMAC(PSK, nonce_dev || nonce_gw || device_id) if (verify_mac_g() ! 0) { hs_state HANDSHAKE_FAIL; break; } // 派生会话密钥 hkdf_sha256(psk, nonce_dev, nonce_gw, ptk); hs_state HANDSHAKE_AUTH; break; case HANDSHAKE_AUTH: // 组认证包MAC_d HMAC(PSK, nonce_gw || nonce_dev || device_id) uint8_t auth_pkt[8 32]; memcpy(auth_pkt, device_id, 8); hmac_sha256(psk, nonce_gw, nonce_dev, device_id, 8, auth_pkt 8); rf_send_packet(auth_pkt, sizeof(auth_pkt)); hs_state HANDSHAKE_WAIT_CONFIRM; mcu_enter_idle_mode(); break; case HANDSHAKE_WAIT_CONFIRM: // 收到网关确认帧 hs_state HANDSHAKE_DONE; rf_power_off(); mcu_enter_deep_sleep(); break; default: break; } } // 射频接收中断回调 void rf_rx_isr(uint8_t *data, uint16_t len) { // 保存数据清除中断标志 // 唤醒MCU后主循环或事件调度器会重新调用 handshake_task() handshake_task(); }这个骨架有一个很关键的设计点每次调用 ( handshake_task() ) 只做一件事然后要么进入 IDLE 等中断要么主动推进到下一步。这样每次射频发完包等待对端响应的间隙CPU 都处于 IDLE 模式而不是死循环轮询。实测下来这种结构比线性流程写法节省了大约 40% 的握手过程平均电流。另外要注意的是在HANDSHAKE_WAIT_MAC和HANDSHAKE_WAIT_CONFIRM这两个等待状态一定要记得设置超时保护不然对端失联时设备会永远卡在 IDLE 里连定时唤醒都救不回来。我一般用低功耗定时器LPTIM做一个 50ms 的超时窗口超时后直接回到 FAIL 状态关闭射频重新进入 Deep Sleep。4.3 用电流仪实测别相信估算直接测软件写完如果不实测低功耗设计就是纸上谈兵。我测握手功耗的习惯是把设备接入电流分析仪我用的是 Joulescope 或 Nordic PPK2普通万用表根本测不了瞬态电流。触发一次握手抓取从射频上电到握手完成整个过程的电流曲线。把曲线转成 CSV在 Python 里直接算电荷消耗。以 BLE 为例一次完整的配对握手在我的设备上的实测电流曲线大致是广播期间 15mA 左右的脉冲每次脉冲几个毫秒连接建立后在连接事件里做安全握手每次连接事件电流 10mA 左右、持续 3ms。如果连接间隔设置成 100ms一次安全握手的等效平均电流会非常低因为大部分时间设备都处于 2µA 的休眠态。我还习惯把实测值和第二章算出来的理论值放到一起对比。如果实测电荷明显高于理论值多半是代码里某个环节没有真正入睡或者射频模块的 shutdown 功耗没达到数据手册标称值。这种“算账 实测”的闭环是我每次低功耗设计的必做功课。4.4 智能穿戴设备安全握手的完整链路示例最后用一个典型的智能穿戴设备同步场景把整个过程串起来设备平时处于 Deep SleepRTC 定时每 10 分钟唤醒一次。唤醒后 MCU 先读传感器数据然后生成随机数开启射频BLE发起安全握手。握手完成后用派生的 PTK 对当天运动数据进行 AES-CCM 加密通过 GATT 或自定义服务推送给手机 App。传输完成后立即关闭射频重新进入 Deep Sleep。这个流程中低功耗设计的几个关键点分别是传感器采集阶段要尽量使用硬件 FIFO 中断唤醒避免 CPU 轮询安全握手阶段用轻量级挑战-应答替代完整 TLS数据加密阶段用硬件 AES 加速器降低 CPU 占用最后在传输完成到下一次唤醒之间所有外设包括 RTC 的时钟源都要精简到最低功耗模式。5. 实战踩坑录那些低功耗握手过程中的“隐形杀手”5.1 GPIO 悬空带来的漏电这是我最想提醒大家的一条。很多 MCU 进入 Deep Sleep 后如果外部 GPIO 处于高阻输入状态引脚上的电位不确定就会通过内部的保护二极管或输入缓冲形成漏电路径。严重的能把整机待机电流从 3µA 顶上 30µA而且非常难查。排查方法在 Deep Sleep 入口前逐个 GPIO 试每次把一个 GPIO 改成确定的上拉或下拉再用电流仪看整机电流有没有显著下降。一旦发现哪个引脚切换后电流骤降就是它在漏电。我的经验是在进入低功耗前把所有不用的 GPIO 全部配置成模拟输入或带上拉的输出低电平悬挂的引脚比配置成输入的引脚省电得多。5.2 射频模块的 PHY 层重连开销这个坑我是在用 BLE 时踩到的。当时想着连接断开后低功耗最省电于是每次握手前先断开连接、握手时再重新连接结果发现 BLE 重连的开销广播 扫描 连接参数协商比保持一个低频连接事件还要费电。原因在于 BLE 的重连过程要经历多个广播事件和连接请求射频开启时间远大于维持连接间隔内的短唤醒。所以现在我的做法是如果设备需要频繁同步数据保持连接但不开启太大的连接间隔如果同步频率很低比如一天几次断开连接、靠广播触发网关侧建链反而更省。选择哪种策略一定要根据“连接保持电流 × 时长”和“断开重连的额外电流”做对比不要想当然。5.3 密钥存储与 Flash 读保护安全握手的根基在于 PSK 的保密性但很多开发者把密钥明文写在 Flash 里RDP 等级也不设置用烧录器一读就能拿到。哪怕协议设计再强密钥泄露等于城门大开。我的建议是量产固件里开启 MCU 的读保护HC32F460 有 RDP 等级、RT1050 有 eFuse 配置条件允许的话把 PSK 存储在独立的安全元件或 TrustZone 保护的区域如果只能放 Flash至少做一次 XOR 或 CRC 混淆避免直接用十六进制字符串明文存储。5.4 AES-CCM 的 L 参数踩坑前面提到 CCM 模式只需要 AES 加密方向但很多人在配置 CCM 的 ( L )长度字段宽度参数时踩坑。( L ) 表示消息长度字段的字节数取值范围是 2~8如果发送端配成 ( L2 )接收端配成 ( L4 )两边算出来的认证标签完全不同握手直接失败。排查这种问题最直接的方法是打日志比较两边的 Nonce 构造CCM 的 Nonce 通常包括 1 字节 Flags、若干字节 Nonce、若干字节长度字段。如果两边组合出的 B0 分块不一致认证失败就是必然的。我在调试时会把 B0 的每一字节都打出来比对这样很快能定位是 flags 配错还是长度字段宽度不一致。5.5 RTC 晶振误差导致的同步失败这个坑相对隐蔽。低功耗握手依赖双方时间同步的场景很常见比如用时间戳做防重放的方案。有些设备为了省电用的是 32.768kHz 低速晶振但这类晶振的精度通常只有 20ppm 左右。如果设备长期处于 Deep Sleep一个月后时间可能偏了接近一分钟。如果网关对时间窗口的校验太严格握手就会直接失败。我的解决思路是两层一是握手协议里尽量用独立随机数防重放不要依赖时间戳二是如果必须用时间戳网关侧校验窗口放宽到 ±5 分钟同时设备每次成功同步后校准一次 RTC 晶振的偏差把长期漂移校准回来。5.6 中断唤醒后的时钟稳定时间被忽略还有一个很容易被忽略的点MCU 从 Deep Sleep 唤醒后内部时钟源可能需要几百微秒甚至几毫秒才能稳定。如果你在中断服务函数里立刻就去操作外设有可能读到的是不稳定的时钟频率。比如 HC32F460 从 Stop 模式唤醒后如果还在用内部 HRC需要等待 HRC 稳定标志置位后再继续跑高精度时序。这个坑在低功耗安全握手里尤其危险因为握手过程有严格的重传超时和报文间隔要求。如果唤醒后时钟不准Timer 定时就会偏可能还没等到网关的回复就超时失败。解决方法是唤醒后先做一次时钟源切换/稳定等待再启动任何和时序相关的操作。6. 写在最后的一些个人心得做低功耗安全握手这么几年我最大的体会是低功耗不是某一个模式或某一块芯片带来的而是一种贯穿全链路的预算思维。你从硬件选型、MCU 低功耗状态、射频协议栈、握手协议裁剪到代码状态机、密钥存储、唤醒时序每一处都在“省钱”最后账面上的那几百微安平均电流、那多出来的几个月续航就是这么一毫安一微安抠出来的。安全方面也一样不是堆上越重的算法就越安全。在电池类设备上最合适的安全方案是恰到好处的预共享密钥 随机数挑战 HMAC 认证 AES-CCM 加密这套组合在我做过的手环、体脂秤、环境监测传感器上都验证过安全性够用、功耗可控、代码也不用那么臃肿。如果以后你做类似的项目我建议从状态机设计和功耗预算表开始把每一步的耗时和电流先估清楚再动代码。先把账算明白把协议剪到最短再把 M CU 每个外设的功耗压到极致这样一次握手的成本就会很低。最后记住所有的低功耗设计都别急着相信数据手册直接拿电流仪测用数据说话这才是工程师最可靠的底气。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价