Files
STM32F4-Base/test/host/ftp_auth/ftp_auth_test.c
T
wandering1 d1e6bb4ba7 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 已销项。
2026-10-11 18:50:24 +08:00

204 lines
8.2 KiB
C
Raw 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.
/*
* 模块名称:FTP 鉴权状态机(lftpd_auth)主机侧单元测试
* 模块功能:在 PC 上对 Drivers/BSP/NET/lftpd/lftpd_auth.c 做离线验证
* (用户名匹配 / 口令校验 / 失败计数 / 限时锁定 / tick 回绕)。
*
* 被测对象:真实源文件 lftpd_auth.c + 其依赖 sha256.c(均以 #include 原样
* 纳入,保证测的是生产代码而非副本;两模块零平台依赖,无需桩——
* tick 由参数注入正是本模块的纯函数设计意图)。
*
* 为什么要主机侧单测(而不是只用台架):
* 锁定 60 s / 解锁 / 成功清零 / tick 32 位回绕是"防口令爆破"契约的核心,
* 板端每验一条要真等 60 s 且回绕要跑满 49.7 天;主机侧注入合成 tick
* 毫秒级穷举。用户名侧信道路径(错用户名也走哈希)同属纯逻辑。
*
* 覆盖点:
* TC-FA-001 user_match:相等 / 大小写不敏感 / 不等 / NULL
* TC-FA-002 正确用户名+正确口令 → OK,计数 0
* TC-FA-003 错口令 → FAIL,计数 1
* TC-FA-004 错用户名+对口令 → FAIL(哈希路径已走,时间拉平)
* TC-FA-005 连续 5 次失败 → 第 6 次(锁定期内)LOCKED,对口令也拒
* TC-FA-006 锁定期满(+60000 tick)→ 对口令 OK,计数清零
* TC-FA-007 成功清零计数:3 败 + 1 成 → 再 4 败不触发锁定(第 5 败才锁)
* TC-FA-008 tick 32 位回绕:锁定期内回绕点仍 LOCKED,期满回绕点放行
* TC-FA-009 配置 pwd_hash 非法 hex → 恒 FAIL(fail-closed)
* TC-FA-010 空口令 / NULL 口令 → FAIL;NULL creds/state → FAIL
*
* 通过:全部 TC PASS(main 返回 0)。
* 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。
*
* 创建日期:2026-10-11
*/
#include <stdio.h>
#include <string.h>
/* 生产源文件原样纳入(顺序敏感:先 sha256 后 lftpd_auth) */
#include "sha256.c"
#include "lftpd_auth.c"
/* ===================== 断言框架 ===================== */
static int s_total;
static int s_passed;
static int s_failed;
#define CHECK(cond, name) do { \
s_total++; \
if (cond) { s_passed++; printf(" PASS %s\n", name); } \
else { s_failed++; printf(" FAIL %s (line %d)\n", name, __LINE__); } \
} while (0)
/* 与 ftp_cfg.h FTP_CFG_DEF_HASH 同源钉值(SHA256("adminadmin")) */
#define DEF_HASH "d82494f05d6917ba02f7aaa29689ccb444bb73f20380876cb05d1f37537b7892"
static const ftp_auth_creds_t s_creds = { "admin", DEF_HASH, 1u };
/* ===================== TC-FA-001:user_match ===================== */
static void tc_user_match(void)
{
CHECK(ftp_auth_user_match("admin", "admin") == 1, "TC-FA-001a 相等匹配");
CHECK(ftp_auth_user_match("admin", "ADMIN") == 1, "TC-FA-001b 大小写不敏感");
CHECK(ftp_auth_user_match("admin", "root") == 0, "TC-FA-001c 不等拒绝");
CHECK(ftp_auth_user_match("admin", "admin2") == 0, "TC-FA-001d 前缀不算匹配");
CHECK(ftp_auth_user_match("admin", NULL) == 0, "TC-FA-001e NULL 拒绝");
}
/* ===================== TC-FA-002..004:基本判定 ===================== */
static void tc_basic(void)
{
ftp_auth_state_t st;
(void)memset(&st, 0, sizeof(st));
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, 1000u) == FTP_AUTH_OK,
"TC-FA-002 对用户名+对口令 → OK");
CHECK(st.fail_count == 0u, "TC-FA-002b 成功后计数为 0");
(void)memset(&st, 0, sizeof(st));
CHECK(ftp_auth_check(&s_creds, 1u, "wrong", &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-003 错口令 → FAIL");
CHECK(st.fail_count == 1u, "TC-FA-003b 失败计数 1");
(void)memset(&st, 0, sizeof(st));
CHECK(ftp_auth_check(&s_creds, 0u, "admin", &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-004 错用户名+对口令 → FAIL(哈希路径已走)");
}
/* ===================== TC-FA-005..006:锁定与解锁 ===================== */
static void tc_lock(void)
{
ftp_auth_state_t st;
uint32_t t = 10000u;
(void)memset(&st, 0, sizeof(st));
for (int i = 0; i < 5; i++) {
CHECK(ftp_auth_check(&s_creds, 1u, "wrong", &st, t) == FTP_AUTH_FAIL,
"TC-FA-005 第 1..5 次失败 → FAIL");
}
CHECK(st.fail_count == 5u, "TC-FA-005b 计数到达 FAIL_MAX");
/* 锁定期内:对口令也 LOCKED,且不再计数 */
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, t + 1u) == FTP_AUTH_LOCKED,
"TC-FA-005c 锁定期内对口令 → LOCKED");
CHECK(st.fail_count == 5u, "TC-FA-005d LOCKED 不改变计数");
/* 59999 tick:仍在窗内(差值 < 60000) */
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st,
st.lock_until_tick - 1u) == FTP_AUTH_LOCKED,
"TC-FA-005e 窗内最后 1 tick 仍 LOCKED");
/* 期满:恰好到达 lock_until → 放行 */
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st,
st.lock_until_tick) == FTP_AUTH_OK,
"TC-FA-006 期满对口令 → OK");
CHECK(st.fail_count == 0u, "TC-FA-006b 解锁成功后计数清零");
}
/* ===================== TC-FA-007:成功清零计数 ===================== */
static void tc_reset(void)
{
ftp_auth_state_t st;
uint32_t t = 5000u;
(void)memset(&st, 0, sizeof(st));
for (int i = 0; i < 3; i++) {
(void)ftp_auth_check(&s_creds, 1u, "wrong", &st, t);
}
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, t) == FTP_AUTH_OK,
"TC-FA-007a 3 败后成功 → OK");
CHECK(st.fail_count == 0u, "TC-FA-007b 成功清零");
for (int i = 0; i < 4; i++) {
CHECK(ftp_auth_check(&s_creds, 1u, "wrong", &st, t) == FTP_AUTH_FAIL,
"TC-FA-007c 清零后再 4 败仍 FAIL(未锁)");
}
CHECK(ftp_auth_check(&s_creds, 1u, "wrong", &st, t) == FTP_AUTH_FAIL,
"TC-FA-007d 第 5 败 FAIL 并上锁");
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, t + 1u) == FTP_AUTH_LOCKED,
"TC-FA-007e 连续 5 败后 LOCKED");
}
/* ===================== TC-FA-008:tick 32 位回绕 ===================== */
static void tc_wraparound(void)
{
ftp_auth_state_t st;
/* 手工构造:锁定截止在 0xFFFFFFF0(距 32 位翻转仅 16 tick) */
(void)memset(&st, 0, sizeof(st));
st.fail_count = 5u;
st.lock_until_tick = 0xFFFFFFF0u;
/* 回绕前 240 tick:仍在窗内 */
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, 0xFFFFFF00u) == FTP_AUTH_LOCKED,
"TC-FA-008a 回绕前窗内 → LOCKED");
/* 回绕后 32 tick:now=0x10,(int32_t)(0x10-0xFFFFFFF0)=+32 → 期满 */
CHECK(ftp_auth_check(&s_creds, 1u, "admin", &st, 0x00000010u) == FTP_AUTH_OK,
"TC-FA-008b 回绕后期满 → OK(有符号差抗回绕)");
}
/* ===================== TC-FA-009..010:fail-closed 边界 ===================== */
static void tc_fail_closed(void)
{
ftp_auth_state_t st;
ftp_auth_creds_t bad_creds = { "admin", "zz2494f05d6917ba02f7aaa29689ccb444bb73f20380876cb05d1f37537b7892", 1u };
(void)memset(&st, 0, sizeof(st));
CHECK(ftp_auth_check(&bad_creds, 1u, "admin", &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-009 非法 hex 配置 → 恒 FAIL(无人能登录,fail-closed)");
(void)memset(&st, 0, sizeof(st));
CHECK(ftp_auth_check(&s_creds, 1u, "", &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-010a 空口令 → FAIL");
CHECK(ftp_auth_check(&s_creds, 1u, NULL, &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-010b NULL 口令 → FAIL");
CHECK(ftp_auth_check(NULL, 1u, "admin", &st, 1000u) == FTP_AUTH_FAIL,
"TC-FA-010c NULL creds → FAIL");
CHECK(ftp_auth_check(&s_creds, 1u, "admin", NULL, 1000u) == FTP_AUTH_FAIL,
"TC-FA-010d NULL state → FAIL");
}
/* ===================== main ===================== */
int main(void)
{
printf("=== FTP Auth Tests ===\n");
printf("-- user match --\n");
tc_user_match();
printf("-- basic check --\n");
tc_basic();
printf("-- lock / unlock --\n");
tc_lock();
printf("-- success reset --\n");
tc_reset();
printf("-- tick wraparound --\n");
tc_wraparound();
printf("-- fail-closed --\n");
tc_fail_closed();
printf("\n=== FTP Auth Tests: %d/%d PASSED (failed=%d) ===\n",
s_passed, s_total, s_failed);
return (s_failed == 0) ? 0 : 1;
}