/* * 模块名称:SHA-256 主机侧单元测试 * 模块功能:在 PC 上对 App/util/sha256.c 做离线验证(FIPS 180-4 标准向量 + * 流式等价 + sha256_hex_eq 边界)。 * * 被测对象:真实源文件 ../../../App/util/sha256.c(以 #include 原样纳入, * 保证测的是生产代码而非副本;本模块零平台依赖,无需任何桩)。 * * 为什么要主机侧单测(而不是只靠台架): * sha256 是 FTP 鉴权的 pwd_hash 校验地基——哈希算错的后果是 * "正确口令被拒 / 错误口令放行"这类静默语义错误,板端只看到 530/230, * 看不出哪一步坏;标准向量在主机侧毫秒级钉死。 * * 覆盖点: * TC-SHA256-001 FIPS 180-4 向量:空串 * TC-SHA256-002 FIPS 180-4 向量:"abc" * TC-SHA256-003 FIPS 180-4 向量:56 字节(补位跨分组边界) * TC-SHA256-004 FIPS 180-4 向量:1,000,000 × 'a'(流式,长度字段进第二分组) * TC-SHA256-005 流式任意切分与一次性计算等价(逐字节喂入) * TC-SHA256-006 hex_eq:小写匹配 / 大写匹配 * TC-SHA256-007 hex_eq:内容不等 / 非法字符 / 长度 63/65 / NULL → 全 0 * TC-SHA256-008 默认口令哈希钉值:SHA256("adminadmin") = FTP_CFG_DEF_HASH 同源值 * * 通过:全部 TC PASS(main 返回 0)。 * 入口:int main(void),返回 0=PASS / 1=FAIL(由 run 脚本判读)。 * * 创建日期:2026-10-11 */ #include #include /* 生产源文件原样纳入(零平台依赖,直接编) */ #include "sha256.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) /* 摘要 → hex 小写串(仅测试用,允许栈上 sprintf) */ static void digest_to_hex(const uint8_t d[SHA256_DIGEST_SIZE], char out[65]) { static const char hexd[] = "0123456789abcdef"; for (int i = 0; i < 32; i++) { out[2 * i] = hexd[(d[i] >> 4) & 0xF]; out[2 * i + 1] = hexd[d[i] & 0xF]; } out[64] = '\0'; } static int hex_eq_vector(const uint8_t d[SHA256_DIGEST_SIZE], const char *want) { char got[65]; digest_to_hex(d, got); return strcmp(got, want) == 0; } /* ===================== TC-SHA256-001..004:标准向量 ===================== */ static void tc_vectors(void) { uint8_t d[SHA256_DIGEST_SIZE]; sha256_calc("", 0u, d); CHECK(hex_eq_vector(d, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"), "TC-SHA256-001 empty-string vector"); sha256_calc("abc", 3u, d); CHECK(hex_eq_vector(d, "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"), "TC-SHA256-002 \"abc\" vector"); sha256_calc("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56u, d); CHECK(hex_eq_vector(d, "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"), "TC-SHA256-003 56-byte vector (padding crosses block)"); { sha256_ctx_t ctx; sha256_init(&ctx); for (int i = 0; i < 1000000; i++) { sha256_update(&ctx, "a", 1u); } sha256_final(&ctx, d); } CHECK(hex_eq_vector(d, "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"), "TC-SHA256-004 1e6 x 'a' vector (streaming)"); } /* ===================== TC-SHA256-005:流式等价 ===================== */ static void tc_stream_equiv(void) { static const char msg[] = "the quick brown fox jumps over the lazy dog"; uint8_t d1[SHA256_DIGEST_SIZE]; uint8_t d2[SHA256_DIGEST_SIZE]; sha256_ctx_t ctx; sha256_calc(msg, strlen(msg), d1); sha256_init(&ctx); for (size_t i = 0; i < strlen(msg); i++) { sha256_update(&ctx, &msg[i], 1u); } sha256_final(&ctx, d2); CHECK(memcmp(d1, d2, sizeof(d1)) == 0, "TC-SHA256-005 byte-by-byte feed equals one-shot"); } /* ===================== TC-SHA256-006..007:hex_eq ===================== */ static void tc_hex_eq(void) { uint8_t d[SHA256_DIGEST_SIZE]; char lower[65]; char upper[65]; char shortv[65]; char longv[66]; sha256_calc("adminadmin", 10u, d); digest_to_hex(d, lower); for (int i = 0; i < 64; i++) { upper[i] = (char)((lower[i] >= 'a') ? (lower[i] - 32) : lower[i]); } upper[64] = '\0'; CHECK(sha256_hex_eq(d, lower) == 1, "TC-SHA256-006a lowercase hex matches"); CHECK(sha256_hex_eq(d, upper) == 1, "TC-SHA256-006b uppercase hex matches (case-insensitive)"); lower[63] = (char)((lower[63] == '0') ? '1' : '0'); CHECK(sha256_hex_eq(d, lower) == 0, "TC-SHA256-007a content mismatch -> 0"); lower[63] = (char)((lower[63] == '0') ? '1' : '0'); /* 还原 */ { char bad[65]; memcpy(bad, lower, 64); bad[64] = '\0'; bad[10] = 'g'; CHECK(sha256_hex_eq(d, bad) == 0, "TC-SHA256-007b invalid hex char -> 0"); } memcpy(shortv, lower, 63); shortv[63] = '\0'; CHECK(sha256_hex_eq(d, shortv) == 0, "TC-SHA256-007c length 63 -> 0"); memcpy(longv, lower, 64); longv[64] = 'a'; longv[65] = '\0'; CHECK(sha256_hex_eq(d, longv) == 0, "TC-SHA256-007d length 65 -> 0"); CHECK(sha256_hex_eq(d, NULL) == 0, "TC-SHA256-007e NULL -> 0"); } /* ===================== TC-SHA256-008:默认口令哈希钉值 ===================== */ static void tc_default_hash(void) { uint8_t d[SHA256_DIGEST_SIZE]; /* 与 ftp_cfg.h FTP_CFG_DEF_HASH 同源钉值(两处若漂移本测试立刻红) */ sha256_calc("adminadmin", 10u, d); CHECK(sha256_hex_eq(d, "d82494f05d6917ba02f7aaa29689ccb444bb73f20380876cb05d1f37537b7892") == 1, "TC-SHA256-008 SHA256(\"adminadmin\") = factory default pwd_hash"); } /* ===================== main ===================== */ int main(void) { printf("=== SHA-256 Tests ===\n"); printf("-- FIPS 180-4 vectors --\n"); tc_vectors(); printf("-- streaming equivalence --\n"); tc_stream_equiv(); printf("-- hex_eq --\n"); tc_hex_eq(); printf("-- default password hash --\n"); tc_default_hash(); printf("\n=== SHA-256 Tests: %d/%d PASSED (failed=%d) ===\n", s_passed, s_total, s_failed); return (s_failed == 0) ? 0 : 1; }