W5波+N-19收口:FTP鉴权落地、Trap25三连定案修复(N-30)、N-20/N-23/N-26

N-19 FTP 鉴权:
- lftpd_auth(USER/PASS 状态机 + 连续5次失败锁60s)+ SHA-256
  (user||pwd) 口令哈希;config.ini [ftp] 段 user/pwd_hash/write,
  出厂 admin/admin 在用有启动告警;lftpd_log.c 删除并入 lftpd_auth
- ftp_cfg 配置读写;台架 ftp_auth_bench.py 13/13

N-30 / Trap 25(CH395F_Trap_Records.md 四次定案全记录):
- 修复1 closed-with-data accept:客户端发完正常 FIN 后芯片按
  FUN_PARA(0x09) §5.52② 保留 Socket+RX 缓冲,旧代码把 CLOSED 一律
  relisten 自毁缓冲 → STOR 必挂 425;改为接受"已关但可读"连接排空
- 修复2 丢 CONNECT 中断探针 net_lost_connect_probe + last_peer_port
  幽灵甄别(拆链窗口握手完成但中断丢失,双信源去抖补 accept)
- 修复3 send_list/send_nlst 批量单发:背靠背 ~60B 小段毒化芯片 TX
  (芯片报 SEND_OK 但对端全丢),整份目录一次性写入
- 证伪存档:固定沉降/Socket 轮换冷却/listen 门控(台架 v7 矩阵)
- 台架工具 test/board/ch395f_reuse_bench.{c,h} + host 脚本存档
  (不注册编译;实验分支 n30-chip-bench 保留)

N-20:Error_Handler 死循环降级(记录+复位路径)
N-23:scripts/check_registration.py 双端注册反向校验(pre-commit)
N-26:GD5F NAND 直 DMA 调用方缓冲窗口断言

验证:BUILD_OK 0/0;ftp_regression 连跑 4 轮 11/11(修复前
LIST#4/#7 100% 挂、STOR#2 曾 100% 425);ftp_auth_bench 13/13;
@test 启动标记 PASS;ci_local L0+L0.5 全过;doc-links 通过。
登记簿 N-30 已销项。
This commit is contained in:
2026-10-11 18:50:24 +08:00
parent eddbedeee8
commit d1e6bb4ba7
75 changed files with 3928 additions and 352 deletions
+253
View File
@@ -0,0 +1,253 @@
/*
* 模块名称:sha256
* 模块功能:SHA-256 安全散列(FIPS 180-4)实现——纯标准库、零堆分配
* 适用平台:STM32F407ZGTx / PC 主机侧单测(双端共用)
* 作者:王建锋
* 创建日期:2026-10-11
* 修改记录:
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权;标准向量由
* test/host/sha256 主机单测钉死)
*/
/* 头文件包含区 */
#include <string.h>
#include "sha256.h"
/* 私有宏定义区 */
#define SHA256_ROTR32(x, n) (((x) >> (n)) | ((x) << (32u - (n))))
#define SHA256_CH(x, y, z) (((x) & (y)) ^ ((~(x)) & (z)))
#define SHA256_MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define SHA256_EP0(x) (SHA256_ROTR32(x, 2u) ^ SHA256_ROTR32(x, 13u) ^ SHA256_ROTR32(x, 22u))
#define SHA256_EP1(x) (SHA256_ROTR32(x, 6u) ^ SHA256_ROTR32(x, 11u) ^ SHA256_ROTR32(x, 25u))
#define SHA256_SIG0(x) (SHA256_ROTR32(x, 7u) ^ SHA256_ROTR32(x, 18u) ^ ((x) >> 3u))
#define SHA256_SIG1(x) (SHA256_ROTR32(x, 17u) ^ SHA256_ROTR32(x, 19u) ^ ((x) >> 10u))
/* 私有变量定义区 */
/* 轮常量(FIPS 180-4 §4.2.2.2:前 64 个素数立方根小数部分前 32 位) */
static const uint32_t s_k[64] = {
0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u,
0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u,
0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu,
0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u,
0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u,
0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u,
0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u,
0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u,
0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u,
};
/* 函数定义区 */
/*
* 函数功能:处理一个 64 B 分组(消息扩展 + 64 轮压缩)
* 入口参数:p_ctx - 上下文 p_block - 64 B 分组(大端字序)
* 返回值:无
* 限定条件:p_block 恰为 64 B 有效数据
* 函数说明:w[64] 为栈上消息扩展数组(256 B,ftpTask 4 KB 栈可容纳)
*/
static void s_transform(sha256_ctx_t *p_ctx, const uint8_t *p_block) {
uint32_t w[64];
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
uint32_t e;
uint32_t f;
uint32_t g;
uint32_t h;
for (uint32_t i = 0u; i < 16u; i++) {
w[i] = ((uint32_t)p_block[4u * i] << 24) |
((uint32_t)p_block[4u * i + 1u] << 16) |
((uint32_t)p_block[4u * i + 2u] << 8) |
(uint32_t)p_block[4u * i + 3u];
}
for (uint32_t i = 16u; i < 64u; i++) {
w[i] = SHA256_SIG1(w[i - 2u]) + w[i - 7u] +
SHA256_SIG0(w[i - 15u]) + w[i - 16u];
}
a = p_ctx->state[0];
b = p_ctx->state[1];
c = p_ctx->state[2];
d = p_ctx->state[3];
e = p_ctx->state[4];
f = p_ctx->state[5];
g = p_ctx->state[6];
h = p_ctx->state[7];
for (uint32_t i = 0u; i < 64u; i++) {
uint32_t t1 = h + SHA256_EP1(e) + SHA256_CH(e, f, g) + s_k[i] + w[i];
uint32_t t2 = SHA256_EP0(a) + SHA256_MAJ(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
p_ctx->state[0] += a;
p_ctx->state[1] += b;
p_ctx->state[2] += c;
p_ctx->state[3] += d;
p_ctx->state[4] += e;
p_ctx->state[5] += f;
p_ctx->state[6] += g;
p_ctx->state[7] += h;
}
/*
* 函数功能:初始化上下文(清零 + 置初始链接变量,FIPS 180-4 §5.3.3)
* 入口参数:p_ctx - 上下文
* 返回值:无
* 限定条件:p_ctx 为 NULL 时静默返回(fail-safe)
*/
void sha256_init(sha256_ctx_t *p_ctx) {
if (p_ctx == NULL) {
return;
}
(void)memset(p_ctx, 0, sizeof(*p_ctx));
/* 初始链接变量(FIPS 180-4 §5.3.3:前 8 个素数平方根小数部分前 32 位) */
p_ctx->state[0] = 0x6a09e667u;
p_ctx->state[1] = 0xbb67ae85u;
p_ctx->state[2] = 0x3c6ef372u;
p_ctx->state[3] = 0xa54ff53au;
p_ctx->state[4] = 0x510e527fu;
p_ctx->state[5] = 0x9b05688cu;
p_ctx->state[6] = 0x1f83d9abu;
p_ctx->state[7] = 0x5be0cd19u;
}
/*
* 函数功能:流式喂入数据(任意长度,内部按 64 B 分组压缩)
* 入口参数:p_ctx - 上下文 p_data - 数据 len - 字节数
* 返回值:无
* 限定条件:p_ctx/p_data 为 NULL 时静默返回
*/
void sha256_update(sha256_ctx_t *p_ctx, const void *p_data, size_t len) {
const uint8_t *p = (const uint8_t *)p_data;
if ((p_ctx == NULL) || (p == NULL)) {
return;
}
for (size_t i = 0u; i < len; i++) {
p_ctx->buf[p_ctx->buf_len] = p[i];
p_ctx->buf_len++;
if (p_ctx->buf_len >= SHA256_BLOCK_SIZE) {
s_transform(p_ctx, p_ctx->buf);
p_ctx->bitlen += (uint64_t)SHA256_BLOCK_SIZE * 8u;
p_ctx->buf_len = 0u;
}
}
}
/*
* 函数功能:收尾输出 32 B 摘要(补位 + 长度字段 + 末次压缩)
* 入口参数:p_ctx - 上下文 p_out - 32 B 摘要输出
* 返回值:无
* 限定条件:p_ctx/p_out 为 NULL 时静默返回;收尾后上下文清零(防敏感残留)
*/
void sha256_final(sha256_ctx_t *p_ctx, uint8_t p_out[SHA256_DIGEST_SIZE]) {
uint64_t bitlen;
uint8_t pad = 0x80u;
uint8_t zero = 0x00u;
uint8_t len_be[8];
if ((p_ctx == NULL) || (p_out == NULL)) {
return;
}
/* 总长 = 完整分组累计 + 暂存区(必须在补位前定稿) */
bitlen = p_ctx->bitlen + (uint64_t)p_ctx->buf_len * 8u;
(void)sha256_update(p_ctx, &pad, 1u);
while (p_ctx->buf_len != 56u) {
(void)sha256_update(p_ctx, &zero, 1u);
}
for (uint32_t i = 0u; i < 8u; i++) {
len_be[i] = (uint8_t)((bitlen >> (56u - 8u * i)) & 0xFFu);
}
(void)sha256_update(p_ctx, len_be, 8u);
for (uint32_t i = 0u; i < 8u; i++) {
p_out[4u * i] = (uint8_t)(p_ctx->state[i] >> 24);
p_out[4u * i + 1u] = (uint8_t)(p_ctx->state[i] >> 16);
p_out[4u * i + 2u] = (uint8_t)(p_ctx->state[i] >> 8);
p_out[4u * i + 3u] = (uint8_t)(p_ctx->state[i]);
}
(void)memset(p_ctx, 0, sizeof(*p_ctx));
}
/*
* 函数功能:一次性计算 32 B 摘要(init+update+final 便捷封装)
* 入口参数:p_data - 数据 len - 字节数 p_out - 摘要输出
* 返回值:无
* 限定条件:栈上上下文 ≈108 B;NULL 参数由子函数兜底
*/
void sha256_calc(const void *p_data, size_t len, uint8_t p_out[SHA256_DIGEST_SIZE]) {
sha256_ctx_t ctx;
sha256_init(&ctx);
sha256_update(&ctx, p_data, len);
sha256_final(&ctx, p_out);
}
/*
* 函数功能:hex 字符 → 半字节值
* 入口参数:c - 字符 p_out - 输出 0~15
* 返回值:0 - 合法;-1 - 非法 int
* 限定条件:p_out 非空
*/
static int s_hex_nibble(char c, uint8_t *p_out) {
uint8_t v;
if ((c >= '0') && (c <= '9')) {
v = (uint8_t)(c - '0');
} else if ((c >= 'a') && (c <= 'f')) {
v = (uint8_t)(c - 'a') + 10u;
} else if ((c >= 'A') && (c <= 'F')) {
v = (uint8_t)(c - 'A') + 10u;
} else {
return -1;
}
*p_out = v;
return 0;
}
/*
* 函数功能:摘要与 64 位 hex 串恒定时间比较(大小写不敏感)
* 入口参数:p_digest - 32 B 摘要 p_hex - 64 字符 hex 串
* 返回值:1 - 相等;0 - 不等 / 长度非 64 / 非法 hex / NULL
* 限定条件:比较段不短路(XOR 累加器);hex 非法属配置错误,早退不构成口令侧信道
*/
int sha256_hex_eq(const uint8_t p_digest[SHA256_DIGEST_SIZE], const char *p_hex) {
uint8_t acc = 0u;
uint8_t hi;
uint8_t lo;
if ((p_digest == NULL) || (p_hex == NULL)) {
return 0;
}
if (strlen(p_hex) != (SHA256_DIGEST_SIZE * 2u)) {
return 0;
}
for (uint32_t i = 0u; i < SHA256_DIGEST_SIZE; i++) {
if ((s_hex_nibble(p_hex[2u * i], &hi) != 0) ||
(s_hex_nibble(p_hex[2u * i + 1u], &lo) != 0)) {
/* hex 非法 = 配置错误(可信来源),早退不构成口令侧信道 */
return 0;
}
acc |= (uint8_t)(p_digest[i] ^ (uint8_t)((hi << 4) | lo));
}
return (acc == 0u) ? 1 : 0;
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 模块名称:sha256
* 模块功能:SHA-256 安全散列(FIPS 180-4)——流式 + 一次性接口,外加摘要与
* 64 位 hex 串的恒定时间比较;纯标准库、零堆分配,MCU 与主机侧
* 单测共用(无平台依赖)
* 适用平台:STM32F407ZGTx / PC 主机侧单测(双端共用)
* 作者:王建锋
* 创建日期:2026-10-11
* 修改记录:
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权:[ftp] pwd_hash 校验)
*/
#ifndef SHA256_H
#define SHA256_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#define SHA256_DIGEST_SIZE 32u /* 摘要字节数 */
#define SHA256_BLOCK_SIZE 64u /* 分组字节数 */
/* 散列上下文(≈108 B,调用方栈上或静态均可) */
typedef struct {
uint32_t state[8]; /* 链接变量 */
uint64_t bitlen; /* 已处理的完整分组累计比特数 */
uint8_t buf[SHA256_BLOCK_SIZE];/* 未满分组的暂存区 */
uint32_t buf_len; /* 暂存区已用字节数 */
} sha256_ctx_t;
/*
* 函数功能:初始化散列上下文
* 入口参数:p_ctx - 上下文(不可为 NULL) sha256_ctx_t *
* 返回值:无
* 限定条件:无
*/
void sha256_init(sha256_ctx_t *p_ctx);
/*
* 函数功能:喂入一段消息(可多次调用,任意切分)
* 入口参数:p_ctx - 上下文 p_data - 数据 len - 字节数
* 返回值:无
* 限定条件:p_ctx 已 init;p_data 为 NULL 或 len=0 时空操作
*/
void sha256_update(sha256_ctx_t *p_ctx, const void *p_data, size_t len);
/*
* 函数功能:补位收尾并输出 32 B 摘要(调用后上下文被清零,不可续用)
* 入口参数:p_ctx - 上下文 p_out - 32 B 输出缓冲(不可为 NULL)
* 返回值:无
* 限定条件:p_ctx 已 init
*/
void sha256_final(sha256_ctx_t *p_ctx, uint8_t p_out[SHA256_DIGEST_SIZE]);
/*
* 函数功能:一次性计算整段消息的 SHA-256
* 入口参数:p_data - 数据 len - 字节数 p_out - 32 B 输出缓冲
* 返回值:无
* 限定条件:p_data/p_out 非空
*/
void sha256_calc(const void *p_data, size_t len, uint8_t p_out[SHA256_DIGEST_SIZE]);
/*
* 函数功能:恒定时间比较 32 B 摘要与 64 字符 hex 串(大小写不敏感)
* 入口参数:p_digest - 32 B 摘要 p_hex - 64 字符 hex 串(可 NULL)
* 返回值:1 - 相等;0 - 不等 / hex 非法 / 参数为空 int
* 函数说明:hex 来自配置文件(可信来源),格式非法早退不构成侧信道;
* 摘要与 hex 内容的比较逐字节累积异或,耗时只随长度(固定 64)变化
*/
int sha256_hex_eq(const uint8_t p_digest[SHA256_DIGEST_SIZE], const char *p_hex);
#ifdef __cplusplus
}
#endif
#endif /* SHA256_H */