DS28E01-100加密芯片C代码包:1-Wire时序与SHA-1防抄板实战 简介DS28E01-100 加密芯片的 C 语言代码包面向需要集成安全芯片的嵌入式开发者与物联网工程师。DS28E01-100 是美信公司的单芯片安全方案内置 SHA-1 硬件加速器常用于身份验证、防克隆和设备密钥存储这套代码可作为驱动或移植参考让开发者绕开底层寄存器操作直接调用接口完成安全功能开发。压缩包共 11 个文件包含 7 个头文件、3 个 C 源文件和 1 份函数说明文档整体仅 21KB。头文件主要声明寄存器、指令和 SHA 算法接口C 源文件则实现单总线通信、SHA 校验及错误处理逻辑doc 文档对 DS28E01-100 的各个函数逐一说明便于查阅和二次开发。已有 1091 人学习下载适合正在评估或使用该芯片的工程师借鉴。通过研读这些代码可以快速理解芯片上电时序、命令流与 SHA 摘要计算过程也能将其中与硬件无关的部分迁移到自己的固件中为产品增加数据完整性校验与防篡改能力。1. DS28E01-100 加密芯片的 C 代码包防抄板该有的正确姿势干嵌入式的大概都碰到过这种需求固件防克隆、耗材防伪、设备配对。最朴素的做法是在 MCU 里写死一串 ID但 ID 能读出来就能被克隆等于没防。DS28E01-100 的思路不一样——秘密放在芯片内部主控通过 1-Wire 总线和它做 SHA-1 挑战应答全程不输出密钥本身。我拆的这份 DS28E01-100-C-code.zip解压后是完整的 C 工程参考1-Wire 时序、DS28E01 命令层、SHA-1 校验层三层分开直接往自己的 STM32、GD32 或者其它 MCU 工程里移植就行。适合正在做防抄板、耗材认证或设备配对的嵌入式工程师。下面按我实际使用的顺序讲底层时序怎么调EEPROM 怎么读写认证链路怎么通最后是踩坑记录。2. 先把 1-Wire 底层扒干净时序窗口、ROM 命令与端口抽象1-Wire 总线的名字很直白一根数据线既传数据又传命令DS28E01-100 的供电也常常靠这根线寄生供电。协议本身不复杂但所有东西都压在时序上主机把总线拉低多久、释放多久、在哪个时间点采样直接决定通信成败。换句话说1-Wire 底层没有“协议栈”只有一套严格的时间窗口。这份代码包把底层做成 ow_driver.c隔离了平台相关的引脚操作我从这段开始讲。2.1 复位与读写时隙代码能跑的前提是每个延时都准DS28E01-100 的所有通信都以复位脉冲开始。主机先把总线拉低一段时间释放后从机在限定的窗口内拉低总线回应一个存在脉冲。这个窗口的宽容度其实不小但很多移植翻车恰恰出在这里——延时函数本身不准窗口就偏了。时序参数手册标准值我实测用的值复位低电平480~960 μs600 μs复位后采样点15~60 μs70 μs略放宽写 0 时隙低电平60~120 μs70 μs写 1 时隙低电平1~15 μs6 μs读时隙采样点10~15 μs12 μs// 复位总线并检测存在脉冲返回 1 表示检测到从机 uint8_t ow_reset(void) { uint8_t presence; OW_OUT_LOW(); delay_us(600); // 复位脉冲手册 480~960us取 600us OW_OUT_RELEASE(); // 释放总线靠上拉电阻拉高 delay_us(70); // 等从机拉低总线采样窗口略放宽 presence OW_IN_READ(); // 读到 0 说明从机应答了 delay_us(410); // 补齐整个复位时隙的时长 return (presence 0) ? 1 : 0; }这段代码的逻辑是拉低 600μs 让从机识别复位释放后从机如果在线会在 15~60μs 内把总线拉低并保持 60~240μs。采样点我特意放在释放后 70μs比手册推荐值略宽原因是不少单片机板子上的上拉电阻、线缆电容会把信号边沿拖缓采样太早容易读到高电平误判从机离线。如果你用的延时函数在 100MHz 主频下误差超过 10%先把延时校准再做别的。读位和写位是更细的活。写 1 和写 0 的差别只在低电平持续时间读位则要在主机释放总线后的极短窗口内完成采样void ow_write_bit(uint8_t bit) { if (bit) { OW_OUT_LOW(); delay_us(6); // 写 1低电平 1~15us然后释放 OW_OUT_RELEASE(); delay_us(64); } else { OW_OUT_LOW(); delay_us(60); // 写 0低电平保持 60~120us OW_OUT_RELEASE(); delay_us(10); } } uint8_t ow_read_bit(void) { uint8_t val; OW_OUT_LOW(); delay_us(2); // 拉低 1~6us启动读时隙 OW_OUT_RELEASE(); delay_us(11); // 释放后 10~15us 内是采样窗口 val OW_IN_READ(); delay_us(50); // 等剩余时隙走完 return val; }这里最容易被忽视的是读时隙的采样点。如果你把采样放在释放后 30μs读到的已经不是当前位而是下一个时隙的起始状态现象是读出的字节看着有规律但全是错位数据。我调过一块板子读 0xAA 永远得到 0x55后来发现就是 GPIO 翻转函数里多包了一层 HAL 调用延迟硬生生多了 18μs。底层函数里不要放任何中断保护、日志输出引脚操作直接操作寄存器。2.2 跳过 ROM 还是匹配 ROM单芯片与多芯片总线的选择1-Wire 总线上可以挂多个从机每个 DS28E01-100 出厂时烧了 64 位 ROM ID家族码 48 位序列号 CRC。通信前必须先“点名”让总线上只有一个从机响应后续命令。点名方式有两种总线上只有一个从机时用跳过 ROM0xCC省去 8 字节 ID 传输有多个从机时必须先搜索 ROM0xF0拿到所有 ID再每次用匹配 ROM0x55选中目标。void ow_skip_rom(void) { ow_write_byte(0xCC); // 跳过 ROM不关心总线上是谁全部响应 } void ow_match_rom(const uint8_t rom_id[8]) { ow_write_byte(0x55); // 匹配 ROM只有 ID 匹配的从机响应该事务 for (int i 0; i 8; i) ow_write_byte(rom_id[i]); }匹配 ROM 的 8 字节 ID 按 LSB-first 顺序逐字节发送也就是手册上 ROM ID 的最低位字节先发。代码包里 ow_search_rom() 做的是标准的三拍搜索每读一位前先发 0xF0然后依次读该位的两个状态从机 A 的位值和从机 B 的位值根据这两位组合决定写入的选择位。如果两位都是 1说明总线上没有从机在该位为 0继续下一位如果某一位读到 00说明两个从机在此位发生了分叉搜索算法会选一条路走下去。这块代码在包里是完整实现我实际用的时候只改了一点——把最大搜索深度从 64 位改成只搜到 8 字节就停因为每台设备只认一个 DS28E01-100。单芯片场景下别用搜索直接 0xCC 跳过一次认证事务能省下 8 字节的 ROM ID 传输时间。1-Wire 标准速度下每字节约 7.5ms8 字节就是 60ms在产线上批量测试时这个差距很明显。2.3 驱动接口抽象换单片机只改三个函数这份代码包能快速落到不同平台靠的是把总线操作抽象成一层接口。ow_driver.c 里不直接出现 STM32 的 HAL 函数而是通过宏映射到具体引脚操作。我拿到代码后第一件事就是把这几个宏替换成自己板子的 GPIO#define OW_OUT_LOW() HAL_GPIO_WritePin(OW_GPIO_Port, OW_Pin, GPIO_PIN_RESET) #define OW_OUT_RELEASE() HAL_GPIO_WritePin(OW_GPIO_Port, OW_Pin, GPIO_PIN_SET) #define OW_IN_READ() HAL_GPIO_ReadPin(OW_GPIO_Port, OW_Pin)只要保证这三个操作用的是同一个引脚且方向切换正确输出释放后要能读输入上层代码完全不用动。如果你手头平台没有硬件定时器也可以用 UART 模拟 1-Wire常见做法是把波特率设在 115200通过发送特定字节产生对应的时隙波形但那个方案对 UART 空闲电平、字节间隔都有要求通用性不如 GPIO 模拟。我自己的习惯是 GPIO 模拟 一个硬件定时器做微秒延时整条总线只占一个引脚剩下的事交给上层驱动。3. DS28E01-100 驱动封装EEPROM 读写与暂存器三步流程底层时序通了DS28E01-100 本身的命令就很好理解了。这颗芯片本质是一个 128 字节 EEPROM 加一个 SHA-1 引擎所有功能通过 8 个命令码触发。命令码本身不复杂真正容易出问题的是数据组织方式和写入流程。3.1 命令集合与存储结构8 个命令码搞定全部功能命令码名称作用后续数据0x0FWrite Scratchpad写暂存器2 字节目标地址 8 字节数据0xAARead Scratchpad读暂存器返回地址 数据 状态 CRC80x55Copy Scratchpad暂存器复制到 EEPROM2 字节目标地址0xC3Write SHA-1 Scratchpad写暂存器并参与 SHA 计算2 字节地址 8 字节数据0x48Compute SHA-1 MAC计算认证码3 字节挑战值0xA5Read MAC读 20 字节 MAC无0x66Read EEPROM读 EEPROM 内容2 字节地址 数据 CRC160x99Release释放总线无DS28E01-100 的 EEPROM 是 128 字节按 8 字节一块组织。写操作的最小粒度就是一个块你不能只写其中的 1 个字节必须凑满 8 字节一起写。目标地址由 TA1 和 TA2 两个字节组成其中 TA1 是块内偏移的起始字节地址0x00~0x7FTA2 是高位地址字节128 字节容量下 TA2 恒为 0x00。这颗芯片写之前还有个特殊点写入内容要先经过暂存器。EEPROM 本体不接收直接写入所有写操作都是“先写暂存器→校验暂存器→复制到 EEPROM”三段式。3.2 读 EEPROM0x66 命令与 CRC16 校验读 EEPROM 是直读不需要经过暂存器。发命令、发地址然后连续读取数据字节最后读 2 字节 CRC16 做完整性校验。我在代码包里封的这个函数按块读取省去跨块边界处理的麻烦int ds28e01_read_block(uint8_t block, uint8_t data[8]) { if (block 15) return -1; // 128 / 8 16 个块 ow_reset(); ow_skip_rom(); ow_write_byte(0x66); // Read EEPROM 命令 ow_write_byte(block 3); // TA1块号转字节地址 ow_write_byte(0x00); // TA2高字节恒为 0 for (int i 0; i 8; i) data[i] ow_read_byte(); uint8_t crc_l ow_read_byte(); // CRC16 低字节先出 uint8_t crc_h ow_read_byte(); if (ow_crc16(data, 8) ! ((crc_h 8) | crc_l)) return -2; // CRC 校验失败 return 0; } uint16_t ow_crc16(const uint8_t *buf, int len) { uint16_t crc 0; while (len--) { crc ^ *buf; for (int i 0; i 8; i) { if (crc 1) crc (crc 1) ^ 0xA001; // CRC-16/ARC 反射多项式 else crc 1; } } return crc; }注意 block 参数传的是块号 0~15转换到字节地址时左移 3 位。CRC16 多项式是 0xA001反射形式初值 01-Wire 总线上所有多字节数据都是低字节先发所以先读回来的是 CRC 低位。新芯片出厂时 EEPROM 内容全是 0xFF这是正常状态不是故障。我第一次拿到样品时看到全 0xFF 还以为是通信没通后来才发现是空片默认值。3.3 写 EEPROM写暂存器、回读、复制的安全三步写 EEPROM 才是这颗芯片最容易翻车的地方。直接发 Copy 命令而不先写暂存器芯片不会有任何反应数据当然进不去。完整的写流程包含三次独立的 1-Wire 事务int ds28e01_write_block(uint8_t block, const uint8_t data[8]) { uint8_t sp[12]; if (block 15) return -1; // 第 1 步Write Scratchpad 0x0F数据先进暂存器 ow_reset(); ow_skip_rom(); ow_write_byte(0x0F); ow_write_byte(block 3); ow_write_byte(0x00); for (int i 0; i 8; i) ow_write_byte(data[i]); // 第 2 步Read Scratchpad 0xAA回读暂存器并校验 ow_reset(); ow_skip_rom(); ow_write_byte(0xAA); for (int i 0; i 12; i) // TA1 TA2 8 字节数据 状态 sp[i] ow_read_byte(); uint8_t crc ow_read_byte(); if (crc ! ow_crc8(sp, 12)) return -2; // CRC 错 if (memcmp(sp[2], data, 8) ! 0) return -3; // 数据不符 // 第 3 步Copy Scratchpad 0x55触发 EEPROM 编程 ow_reset(); ow_skip_rom(); ow_write_byte(0x55); ow_write_byte(block 3); ow_write_byte(0x00); delay_ms(20); // tPROG 最大约 10ms留足余量 return 0; }为什么非要回读校验因为 1-Wire 总线上任何一次时序抖动都会污染暂存器里的数据而这种污染在写暂存器阶段读不出来——你只知道自己发了什么不知道芯片收到了什么。Read Scratchpad 返回的 12 字节里包含地址、数据、状态全部被 CRC8 覆盖校验通过后再执行 Copy成功率会高很多。CRC8 多项式是 0x8CX^8X^5X^41 的反射形式初值 0代码包里有现成的 ow_crc8()。Copy Scratchpad 期间总线必须保持安静。DS28E01-100 的 EEPROM 编程需要约 2~10ms这时候任何总线活动都可能中断编程过程轻则数据损坏重则整块内容变成随机值。我的做法是 Copy 之后用阻塞延时等 20ms期间禁止一切中断和任务切换。如果你在 RTOS 环境里这里不要用 vTaskDelay 让出 CPU用一个硬件定时器阻塞等待否则调度器切走任务后别的任务碰一下总线引脚就全完了。别问我怎么知道的做过一次污染一整批板子的事。4. SHA-1 认证链路挑战-应答的芯片侧与主机侧实现EEPROM 只是 DS28E01-100 的一半价值它真正值钱的是 SHA-1 认证。这套机制解决的核心问题是如何在不暴露密钥的前提下证明“对面那颗芯片是正品”。4.1 认证原理密钥不出芯片主机只验 MAC很多人刚接触加密芯片会问为什么用 SHA-1 这种老算法而不是 AES原因在于认证和加密是两回事。AES 加密需要把密钥放在通信两端MCU 端固件被 dump 后密钥跟着泄露整个体系崩掉。DS28E01-100 的做法是8 字节密钥secret烧在芯片内部任何命令都读不出它。主机发起认证时丢给芯片一串挑战值芯片把挑战值、暂存器里的数据、内部密钥拼在一起做 SHA-1输出 20 字节 MAC。主机端用同样的数据和自己保存的密钥副本算一遍比对 MAC 是否一致。这里有个必须想明白的点主机侧保存的密钥副本如果只是简单放在固件里攻击者 dump 固件照样能拿到。生产中常见做法是主机侧密钥分散存放、或者由安全元件代为保存至少不要明文字节连续排列在 Flash 里。SHA-1 的 20 字节输出本身不暴露密钥这是它能用于认证的根本原因。4.2 芯片侧流程0x0F、0x48、0xA5 三段式一次典型的芯片侧认证包含三个事务先写暂存器作为认证数据源再发 Compute SHA-1 MAC 命令带挑战值最后读回 20 字节 MAC。int ds28e01_write_scratchpad(uint8_t block, const uint8_t data[8]) { ow_reset(); ow_skip_rom(); ow_write_byte(0x0F); ow_write_byte(block 3); ow_write_byte(0x00); for (int i 0; i 8; i) ow_write_byte(data[i]); return 0; } int ds28e01_compute_mac(uint8_t block, const uint8_t data[8], const uint8_t challenge[3], uint8_t mac[20]) { // 先通过写暂存器把认证数据送入芯片 if (ds28e01_write_scratchpad(block, data) ! 0) return -1; // Compute SHA-1 MAC0x48 3 字节挑战值 ow_reset(); ow_skip_rom(); ow_write_byte(0x48); ow_write_byte(challenge[0]); ow_write_byte(challenge[1]); ow_write_byte(challenge[2]); // Read MAC0xA5芯片返回 20 字节 ow_reset(); ow_skip_rom(); ow_write_byte(0xA5); for (int i 0; i 20; i) mac[i] ow_read_byte(); return 0; }挑战值是 3 字节这是 DS28E01-100 手册里 Compute SHA-1 MAC 命令后固定长度的参数不要多写也不要少写。0x48 命令发出后芯片内部会结合暂存器内容、挑战值和密钥计算 MAC。也有人用 0xC3 Write SHA-1 Scratchpad 替代 0x0F区别在于 0xC3 写暂存器的同时会提前初始化 SHA-1 引擎的中间状态适合把数据本身就当作认证对象反复使用的场景0x48 则适合临时指定挑战值的场景。我倾向于都用 0x48 走统一流程代码逻辑更清晰。4.3 主机侧校验软件 SHA-1 与字节序核对主机侧拿到的 MAC 是 20 字节需要自己用软件 SHA-1 计算一遍预期值做比对。标准 SHA-1 算法代码包里已经带了一份 sha1.c关键是输入数据的拼装顺序——DS28E01-100 内部 SHA-1 计算块的字节排列、挑战值放前还是放后、密钥与数据如何拼接这些细节在数据手册的 SHA-1 章节里有明确的分块图。void host_verify_mini(void) { uint8_t data[8] {0}; // 与写入芯片暂存器的数据一致 uint8_t secret[8] {0}; // 主机侧保存的密钥副本 uint8_t ch[3] {0x12, 0x34, 0x56}; uint8_t mac_chip[20], mac_host[20]; if (ds28e01_compute_mac(0, data, ch, mac_chip) ! 0) { debug_printf(chip side failed\r\n); return; } sha1_init(); sha1_update(data, 8); sha1_update(secret, 8); sha1_update(ch, 3); sha1_final(mac_host); if (memcmp(mac_host, mac_chip, 20) 0) debug_printf(auth pass\r\n); else debug_printf(auth fail\r\n); }上面这个顺序是我这份代码包里的默认拼接方式但你拿到后第一件事应该是对着数据手册的分块图核对一遍而不是直接相信任何现成代码。SHA-1 本体实现几乎不可能错错都在输入顺序上。我调试时习惯把三组输入同时落到文件里用命令行独立算一遍做交叉验证cat data.bin secret.bin challenge.bin combined.bin openssl dgst -sha1 combined.bin用 xxd 查看三个文件的内容是否和代码里发出去的一致尤其是多字节数据的字节序。1-Wire 所有字节都是 LSB-first 发送你代码里看到数组顺序和总线上实际抵达芯片的顺序是一样的但如果你从某个参考代码里抄了一段“高位先发”的 SHA-1 填充逻辑就会和芯片内部的算法对不上。先验证输入再去调 SHA-1 引擎。5. 踩坑记录DS28E01 最常见的五种翻车现场这一章全是被现实毒打出来的经验。每一条我都按“现象 → 原因 → 解决”写你照着排查比自己瞎试快得多。5.1 存在脉冲时有时无复位窗口没对准现象上电后第一次 ow_reset 总是返回 0按一下板子的复位键又好了或者 10 次里有 2~3 次检测不到从机。原因分两种。一种是上电瞬间电源还没稳定DS28E01-100 内部上电复位没有完成这时候拉总线它根本没有反应。另一种是上拉电阻选型问题1-Wire 标准上拉是 4.7kΩ但如果你把总线拉低的时间不够、或者上拉电阻用了 10kΩ释放后总线恢复高电平的速度变慢存在脉冲的采样点就会错过。解决软件上上电后先阻塞延时 100ms 再做第一次复位这一条能消除大多数上电偶发失败硬件上上拉电阻用 4.7kΩVCC 对地加 0.1μF 去耦电容。如果总线上还挂了其他 1-Wire 器件导致寄生电容变大把上拉电阻降到 2.2kΩ 可以加快上升沿代价是功耗略高DS28E01-100 这种低功耗场景完全能接受。5.2 写进去回读全是 0xFFCopy 步骤被忽略现象调用写块函数没报错但回读数据全是 0xFF好像写入根本没发生过。原因最常见的是代码里跳过了写暂存器步骤直接发了 Copy Scratchpad 命令。DS28E01-100 的 EEPROM 不接收直接写暂存器是唯一入口没有数据可复制Copy 自然什么也不做。另一个常见原因是 Copy 后立刻读 EEPROM芯片还在编程中读出来的是旧值。解决严格走 0x0F → 0xAA → 0x55 三步。0x55 之后必须等待编程完成手册标称 tPROG 最大 10ms我统一等 20ms。写完后做一次回读确认回读失败就重试整个流程。EEPROM 编程是物理操作重试不会降低寿命DS28E01-100 的擦写寿命足够产品生命周期使用。5.3 0x55 撞车ROM 层和器件层用了同一个命令码现象调试匹配 ROM 时改了某个宏结果 EEPROM 写入也坏了或者反过来调整 Copy 命令后多芯片匹配也不工作了。原因1-Wire 协议分两层ROM 层的 Match ROM 命令码是 0x55器件层的 Copy Scratchpad 命令码也是 0x55。两颗芯片共用同一个字节但含义完全取决于当前处在协议层的哪个阶段。如果你把所有命令码放在同一个枚举里改一个就会连带影响另一个。解决把命令码按层分开。ROM 层命令放在 ow_rom.h器件层命令放在 ds28e01_cmd.h命名上直接区分 OW_CMD_MATCH_ROM 和 DS28E01_CMD_COPY_SP。这样即使数值相同一眼就能看出用途也不会改串。代码包里的分层就是这么做的你如果自己维护驱动也建议照这个习惯来。5.4 MAC 永远对不上先查字节序再查 SHA-1 填充现象芯片返回的 20 字节 MAC 和主机侧软件算的完全不一致改成任何顺序都不对调了一晚上感觉像是 SHA-1 实现坏了。原因十有八九不是 SHA-1 实现问题。 1-Wire 总线所有字节都是 LSB-first 发送主机侧缓冲区里看到的字节序和总线上的位序是一一对应的但如果你参考的代码在拼装 SHA-1 输入块时做了字节交换或者大小端转换就会产生系统性偏差。另一个高发点是挑战值 3 字节的拼接顺序手册分块图里挑战值的字节位置是固定的不能随意前后调。解决第一步把 data、secret、challenge 三组字节全部打印出来逐字节核对发送端和接收端是否一致。第二步用独立工具对同一份输入文件算 SHA-1对比软件实现是否正确。第三才是对照手册分块图调整拼装顺序。我一般会先加一个 hex dump 函数void hex_dump(const char *tag, const uint8_t *buf, int len) { debug_printf(%s:, tag); for (int i 0; i len; i) debug_printf( %02X, buf[i]); debug_printf(\r\n); }调用时把 data、secret、challenge 都打出来和芯片侧最后读到的暂存器内容放在一起比对能快速缩小问题范围。不要一上来就怀疑 sha1.c那个文件是标准实现出错的概率极低。5.5 总线上混挂 DS18B20没匹配 ROM 就全乱套现象板子上同时挂了 DS18B20 做温度采集DS28E01-100 有时读出来的是温度值有时 EEPROM 数据错位两个器件互相干扰。原因调用 ow_skip_rom() 时总线上所有从机都会响应后续命令。DS18B20 和 DS28E01-100 都收到了 0x66 之类的命令后各自按自己的协议回数据两边同时在总线上拉电平数据直接变成乱码。解决总线上有多个 1-Wire 器件时必须先执行一次完整的 ROM 搜索0xF0把每个从机的 64 位 ROM ID 读出来之后每次操作前用 0x55 匹配目标 ID只让目标芯片响应。如果产品结构允许更省心的做法是把 DS18B20 和 DS28E01-100 放在两个不同的 GPIO 引脚上分成两条独立总线彻底隔离。我后来在量产板上都是这么干的一路总线只挂 DS28E01-100温度传感器走另一路排查问题也简单。6. 挑战值随机化别让 MAC 变成可重放的录音SHA-1 认证流程跑通后还有一个容易被忽略的漏洞如果挑战值是固定常量攻击者只需要在总线上录一次“主机发挑战、芯片回 MAC”的完整通信然后在自己的设备里原样重放这段数据就能骗过主机——因为主机校验的是 MAC 本身不校验挑战值是不是新生成的。这等于把认证降级成了简单的口令比对。解决方法是每次认证都生成不同的挑战值。最直接的随机源是 MCU 内部 ADC 的悬空引脚噪声这在大多数单片机上都可用uint32_t adc_noise_random(void) { uint32_t seed 0; for (int i 0; i 32; i) { HAL_ADC_Start(hadc1); HAL_ADC_PollForConversion(hadc1, 10); uint32_t v HAL_ADC_GetValue(hadc1); seed (seed 1) | (v 1); // 取 ADC 最低位逐个拼进种子 } return seed; }这段代码把 ADC 采样值的最低位逐位拼成 32 位随机数天然适合生成 3 字节挑战值。关键点是随机数要在每次认证时重新采不要在上电时生成一次然后反复用——那和常量没有本质区别。没有 ADC 的场合可以用两个不同频率的定时器读数做异或或者用 RTC 的亚秒值混合一个固定盐效果稍弱但也能挡住录制重放。调试时建议给认证函数加一个测试模式把 challenge 固定为一个单调递增计数器。这样既保持每次不同能防重放又方便定位通信问题——计数器值不对说明挑战值传递链路有问题计数器对但 MAC 错问题在 SHA-1 拼装。等测试模式全部通过再切回真随机数。我最初做第一版防抄板方案时挑战值就是写死的三个字节 0x01 0x02 0x03当时觉得“反正数据在总线上也是加密的”结果被同事用逻辑分析仪录了一段写了个 20 行的 Python 脚本就把认证绕过去了。从那以后我每次发板前的自查清单里都会强制加一项全工程搜索 challenge 赋值处确认不是常量。希望帮到你。本文还有配套的精品资源点击获取