Files
DCU_APP/docs/compose/spec/ecat-dcu-flash-crypto-audit.md

124 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
feature: ecat-dcu-flash-crypto-audit
status: delivered
updated: 2026-09-24
branch: ecat-dcu-flash-crypto-audit
commits: 28d8426..28d8426
---
# EtherCAT 总线刷写 DCU:加密与握手完整性审计
## Report
**What was built** — 对「EtherCAT 总线刷写 DCU」路径完成加密与握手完整性审计(docs-only,无代码改动)。结论:本仓库可见的 APP 交接与布局契约上 **无载荷加密**(仅镜像尾 CRC32);所谓握手是 **0x2502 交接 + 配置页 arm 门**,不是密码学握手,且 arm/令牌语义在 BOOT(本仓库不可验证)、APP 擦页会抹掉 word8 令牌字。与电机 OTA(AES-128 + nonce 会话)严格区分。
**Verification** — 全库 `aes128|encrypt` 仅命中 `sys/aes128.*` + `motor_ota_relay.cpp`(PASS);`config.h:333` word 计数仍为 8(PASS,缺陷);`ota_boot_request.cpp:198` CAN 路径仍同步 `requestEnterBoot`(PASS,缺陷);`ota_boot_request.cpp` 无 `gracefulTeardown`/`fwdgt_*`(PASS,缺陷);`0x2500` 仅注释(PASS,BOOT 盲区);spec self-check PASS。独立审查 FAIL(1 critical 事实错误 + 1 major 范围过宽)→ 修正后 re-review PASS。
**Journey log**
- `clearCrashCount()` 写 `kOtaAval` 走 `writeWord`,**不会**栈越界;OOB 仅擦除路径且 word 下标 ≥8。活缺陷是擦页抹掉 word8/9,不是该调用点。
- BOOT `ecatArmed()` / `0x2500` 会话只在 `dc_app.c` 注释里;凡 BOOT 侧行为必须标「不可验证」,不可用 APP 证据替它下无加密定论。
- 本环境再次拦截 `git worktree add`,落在分支 `ecat-dcu-flash-crypto-audit`(非隔离 worktree)。
- 工作区另有未跟踪/未提交改动(`esc_lan9252.cpp`、`config.h`、`ecat-wkc-periodic-dip.md`),与本单无关,未纳入提交。
## [S1] Problem
需要确认当前 **EtherCAT 总线刷写 DCU 固件** 路径是否具备加密,以及握手/鉴权链路是否完整。该路径与「电机 OTA 中继」(AES-128 + nonce 会话)不同,不能混谈。审计对象为本仓库(`GD32E508_DCU_APP`)可见的 APP 交接入口、共享布局、以及文档/注释中对 BOOT 侧 `0x2500/0x2501` 直灌会话的契约描述。
## [S2] Design
### S2.1 路径范围(唯一真源)
```text
主站 EtherCAT
│
│ SDO 0x2502 = 1 ← APP 字典「GD32 boot request」
▼
EcatEnterBootRequestAsync() ← 只置 pending(异步)
│
▼
OtaBootRequest::process() ← 主循环写配置页 + 软复位
│ CHECK=AVAL, AVAL=AVAL, EN=NOT_AVAL
▼
BOOT 起机(10s OTA 窗口)
│ ecatArmed() = !(CHECK==AVAL && EN==AVAL)
│ 交接后 EN=NOT_AVAL ⇒ 直灌解锁(可不依赖 word8 令牌)
▼
BOOT 对象 0x2500 / 0x2501 ← 固件灌写会话(源码不在本仓库)
│
▼
APP 区镜像 + 末尾 CRC32
```
对照旁路(**不在本次范围**,仅防混淆):
| 路径 | 加密 | 握手 |
|------|------|------|
| 电机 OTA 中继(CAN 0x100/0x101) | AES-128 ECB + keyed-CRC32 | nonce → K_session → confirm 三步 |
| EtherCAT ESI/EEPROM 刷写 | 无 | 无(仅 SII CRC8) |
| **EtherCAT 总线刷写 DCU(本次)** | **见 S2.2** | **见 S2.3** |
### S2.2 加密结论:**本仓库可见路径没有载荷加密**
| 检查项 | 结论 | 证据 |
|--------|------|------|
| 固件载荷 AES/对称加密(APP 可见代码) | **无** | 全库 `aes128|encrypt` 仅命中 `sys/aes128.*` 与 `sys/motor_ota_relay.cpp`;`comm/ssc`/`ota_boot_request`/`dc_app` 零加密调用 |
| 传输层机密性(APP 交接 + 布局契约) | **未见** | `dc_app.c:305-306` 注明直灌会话在 BOOT 的 `0x2500/0x2501`;本仓库可见路径无载荷加密 |
| BOOT `0x2500/0x2501` 是否解密/验签 | **本仓库不可验证** | 源码不在本 repo;不能据 APP 侧断言 BOOT 一定无加密 |
| 完整性校验(布局契约) | **仅有镜像尾 CRC32** | `shared/ota_layout.h:19` `OTA_LAYOUT_APP_MAX_SIZE`(216KB-4B,末尾 CRC32) |
| 源码/固件签名 | **未见** | 无 ECDSA/HMAC/secure boot 引用 |
**一句话(限定范围)**:在 **本仓库可见的 APP 交接与布局契约** 上,EtherCAT 刷写 DCU 是 **明文 SDO 灌写意图 + 镜像尾 CRC32**,**没有载荷加密**;BOOT 侧是否另有加密 **无法在本仓库证实或证伪**。
### S2.3 握手结论:**不是密码学握手,只是交接 + arm 门;链路不完整**
实际存在的「握手」是 **交接(handoff)+ 配置页 arm 门**,不是 challenge-response:
| 步骤 | 完整性 | 证据 |
|------|--------|------|
| 1. APP 交接入口 `0x2502` | **完整(异步)** | `dc_app.c:329-350` `ObjBootReqWrite` → `EcatEnterBootRequestAsync`;`ota_boot_request.cpp:257-260` 只置 pending;Flash/复位在 `process()` (`:209-248`) |
| 2. 配置页 arm 语义 | **注释契约(BOOT 行为,本仓库不可验证)** | `dc_app.c:316-318` 描述 `ecatArmed() = !(CHECK==AVAL && EN==AVAL)`;实现不在 APP |
| 3. word8 令牌 `ECAT_ARM_KEY=0xACA70001` | **按注释可被交接旁路;APP 擦页会毁掉令牌字** | `dc_app.c:316-318` 称交接后不必 word8;`config.h:333` word 计数=8;`ota_boot_request.cpp:54-78` 擦页只回写 8 字 → 页上 word8/9 被抹 |
| 4. nonce / 会话密钥 / 双向认证 | **无** | 对比 `motor_ota_relay.cpp:896-955` 的 Req→Nonce→Confirm |
| 5. BOOT 侧会话对象协议 | **本仓库不可验证** | `dc_app.c:305-306`:`0x2500/0x2501` 在 BOOT 字典;APP 无实现 |
| 6. CAN 旁路 `0x320+"OTA!"` | **不完整** | `ota_boot_request.cpp:187-200` `handleCanFrame` 仍 **同步** `requestEnterBoot()`(VULN-08 未落地) |
**一句话**:所谓握手 = **「让我进 BOOT」+「配置页门禁」**,**无加密握手、无会话密钥、无双向认证**;门禁语义本身在 BOOT(本仓库不可验证),且 APP 擦页会把 word8 令牌字抹掉。
### S2.4 阻断「完整」结论的现存缺陷(APP 侧可证实)
1. **VULN-11 仍在源码(擦页抹字已证实;栈越界仅限擦除路径)**:`sys/config.h:333` `CONFIG_OTA_CONFIG_WORD_COUNT = 8U`,应为 `OTA_LAYOUT_WORD_COUNT`(`shared/ota_layout.h:48` = 10)。`requestEnterBoot()` 对 word2 写 `NOT_AVAL` 会走 `otaConfigSet` 擦除路径(`ota_boot_request.cpp:50-80`):只备份/回写 8 字 → 页上 word8 `ECAT_ARM` / word9 `CRASH_COUNT` 被抹成 `0xFFFFFFFF`。栈 `words[]` 越界写仅发生在 **目标 word 下标 ≥8 且 value==NOT_AVAL** 时(`:58`);`clearCrashCount()`(`:129-131`)写的是 `kOtaAval`,走 `writeWord`(`:50-51`),**不会**触发该越界。
2. **VULN-08 仍在源码**:`ota_boot_request.cpp:187-200` CAN `0x320` 路径在 `taskCanRxParse` 上下文同步 `requestEnterBoot()`(内部 `fmc_page_erase` `:62`,20–30ms),未走 `requestEnterBootAsync`(`:175-181`)。
3. **交接后 Flash 擦写防护未按审计加固落地**(对照 `docs/audit_ecat_spi_wifi_ota_compatibility.md` §3):
- 仍调用 Flash 库 `fmc_word_program`/`fmc_page_erase`(`ota_boot_request.cpp:30,62,73`),非 SRAM 寄存器直写;
- `process()` 在擦写前只关 SPI0/EXTI10_15(`:216-217`);`__disable_irq()` 仅在复位前(`:244`),**不覆盖** `fmc_page_erase` 窗口;
- 未见 FWDGT 复位前扩窗(无 `fwdgt_*`);
- 未见 LAN9252 PREOP / CAN STB / EXMC 优雅关停(无 `gracefulTeardown`)。
4. **BOOT 侧盲区**:`0x2500/0x2501` 是否校验 CRC32、是否接受任意明文、是否有会话状态机,**本仓库无法证明完整**(对象仅出现在 `dc_app.c:305-306` 注释)。
### S2.5 与电机 OTA 的对照(防误读)
| 维度 | 电机 OTA 中继 | EtherCAT 刷写 DCU |
|------|---------------|-------------------|
| 载荷加密 | AES-128 ECB 48B/包 | **APP 可见路径无**(BOOT 不可验证) |
| 密钥协商 | `K_master` + nonce → `K_session` | **无** |
| 确认帧 | confirm = AES(nonce⊕0x55) | **无** |
| 完整性 | keyed-CRC32(session‖plain) | 镜像尾 CRC32 |
| 会话绑定 | session_num + pkt_num | **未见** |
| 传输保护 | ARD 单发 + BRS 压制 + 通道锁 | SDO 邮箱;通道锁与本路径无关 |
## [S3] Out of Scope
- 电机 OTA 中继的 AES/握手实现质量(另有完整状态机,不在本单)。
- EtherCAT ESI/EEPROM(`gen_eeprom.py` / `ecat_flash_eeprom.py`)刷写。
- BOOT 工程 `GD32E50X_BOOT` 内 `0x2500/0x2501` 具体实现(源码不在本仓库)。
- 对上述缺陷的代码修复(本单为「检查」;修复需另开 implement 任务并获确认)。
- 电机/DCU 联合端到端刷写台架实测。
## Tasks
- [x] T1: 梳理 EtherCAT 刷写 DCU 全路径与对象边界 — acceptance: S2.1 流程与对象归属可对照源码/注释逐点定位 (covers: S2.1)
- [x] T2: 判定载荷加密有无 — acceptance: S2.2 表中每行有仓库内证据或明确「未见」 (covers: S2.2)
- [x] T3: 判定握手/arm 链完整性 — acceptance: S2.3 表逐步给出完整/不完整/不可验证,并指出门禁旁路与令牌破坏路径 (covers: S2.3)
- [x] T4: 列出阻断「完整」结论的 APP 侧缺陷 — acceptance: S2.4 各条可指到 `file:line` (covers: S2.4)
- [x] T5: 输出与电机 OTA 的对照 — acceptance: S2.5 无混淆两路径的加密/握手结论 (covers: S2.5)