/* * 模块名称:lftpd_auth * 模块功能:FTP 鉴权状态机实现(纯函数,仅依赖 stdint/string 与 sha256) * 适用平台:STM32F407ZGTx / PC 主机侧单测(双端共用) * 作者:王建锋 * 创建日期:2026-10-11 * 修改记录: * v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权;契约由 * test/host/ftp_auth 主机单测钉死) */ /* 头文件包含区 */ #include #include #include "lftpd_auth.h" #include "sha256.h" /* 函数定义区 */ /* * 函数功能:用户名匹配(大小写不敏感) * 入口参数:p_expect - 配置用户名 p_user - 客户端 USER 参数 * 返回值:1 - 匹配;0 - 不匹配或任一为 NULL int * 限定条件:无 */ int ftp_auth_user_match(const char *p_expect, const char *p_user) { if ((p_expect == NULL) || (p_user == NULL)) { return 0; } for (size_t i = 0u; ; i++) { char a = p_expect[i]; char b = p_user[i]; if ((a >= 'A') && (a <= 'Z')) { a = (char)(a + 32); } if ((b >= 'A') && (b <= 'Z')) { b = (char)(b + 32); } if (a != b) { return 0; } if (a == '\0') { return 1; } } } /* * 函数功能:口令校验 + 失败计数 + 限时锁定(鉴权契约核心) * 入口参数:p_creds - 凭据 user_ok - USER 阶段匹配结果 p_pass - 口令 * p_state - 会话外共享状态 now_tick - 当前 tick(ms) * 返回值:FTP_AUTH_OK / FAIL / LOCKED(枚举) * 限定条件:锁定判定先于哈希(锁定期内不做哈希);用户名错也走哈希(耗时拉平); * tick 有符号差抗 32 位回绕;NULL 入参 fail-closed 返回 FAIL */ ftp_auth_result_t ftp_auth_check(const ftp_auth_creds_t *p_creds, uint8_t user_ok, const char *p_pass, ftp_auth_state_t *p_state, uint32_t now_tick) { sha256_ctx_t ctx; uint8_t digest[SHA256_DIGEST_SIZE]; int ok; if ((p_creds == NULL) || (p_state == NULL)) { return FTP_AUTH_FAIL; /* fail-closed */ } if (p_state->fail_count >= FTP_AUTH_FAIL_MAX) { /* 有符号差:now 在 lock_until 之前(含回绕)= 仍在锁定窗内 */ if ((int32_t)(now_tick - p_state->lock_until_tick) < 0) { return FTP_AUTH_LOCKED; } p_state->fail_count = 0u; /* 锁定期满,重新起算 */ } /* 盐 = 期望用户名;用户名错也走同一哈希路径(耗时拉平,见头文件说明 2) */ sha256_init(&ctx); if (p_creds->p_user != NULL) { sha256_update(&ctx, p_creds->p_user, strlen(p_creds->p_user)); } if (p_pass != NULL) { sha256_update(&ctx, p_pass, strlen(p_pass)); } sha256_final(&ctx, digest); ok = ((user_ok != 0u) && (sha256_hex_eq(digest, p_creds->p_pwd_hash) != 0)) ? 1 : 0; if (ok != 0) { p_state->fail_count = 0u; return FTP_AUTH_OK; } p_state->fail_count++; if (p_state->fail_count >= FTP_AUTH_FAIL_MAX) { p_state->lock_until_tick = now_tick + FTP_AUTH_LOCK_TICKS; } return FTP_AUTH_FAIL; }