diff --git a/src/shared/credentials.c b/src/shared/credentials.c index f1a0c98..7f79c52 100644 --- a/src/shared/credentials.c +++ b/src/shared/credentials.c @@ -158,13 +158,13 @@ static int hex_value(char c) { * digits. Such a line is refused loudly (and never accepted) so an operator * cannot keep a replayable bearer digest in place after the protocol bump. */ static bool secret_is_legacy_hex(const char* s) { - if (!s) + if (!s || strlen(s) != 64) return false; for (int i = 0; i < 64; i++) { if (hex_value(s[i]) < 0) return false; } - return s[64] == '\0'; + return true; } bool credentials_b64_encode(const uint8_t* in, size_t n, char* out, size_t out_sz) { diff --git a/tests/test_credentials.c b/tests/test_credentials.c index 86d4dd6..80280e7 100644 --- a/tests/test_credentials.c +++ b/tests/test_credentials.c @@ -473,6 +473,34 @@ static void test_credentials_store_rejects_legacy_hex() { free(path); } +/* C9: the legacy-hex detector must check the length before indexing 64 bytes, so + * a short secret is never read out of bounds. Such a line is rejected for the + * ordinary "expected verifier" reason, never as legacy. */ +static void test_credentials_store_rejects_short_secret() { + char contents[CREDENTIAL_MAX_LINE]; + snprintf(contents, sizeof(contents), "alice:%s\n", "abc"); + char* path = make_tmp_file(contents); + EXPECT_NOT_NULL(path); + char err[512]; + const CredentialStore* store = credentials_load(path, NULL, err, sizeof(err)); + EXPECT_NULL(store); + EXPECT_TRUE(strstr(err, "legacy unsalted") == NULL); + rm_temp(path); + free(path); + + char short_hex[64]; + memset(short_hex, 'a', 63); + short_hex[63] = '\0'; + snprintf(contents, sizeof(contents), "alice:%s\n", short_hex); + path = make_tmp_file(contents); + EXPECT_NOT_NULL(path); + store = credentials_load(path, NULL, err, sizeof(err)); + EXPECT_NULL(store); + EXPECT_TRUE(strstr(err, "legacy unsalted") == NULL); + rm_temp(path); + free(path); +} + static void test_credentials_store_duplicate_rejected() { char line[CREDENTIAL_MAX_LINE]; EXPECT_TRUE(make_store_line("alice", KAT_PASSWORD, CREDENTIAL_MIN_ITERS, line, sizeof(line))); @@ -1066,6 +1094,7 @@ void test_credentials(void) { test_credentials_store_parse_valid(); test_credentials_store_parse_rejects_malformed(); test_credentials_store_rejects_legacy_hex(); + test_credentials_store_rejects_short_secret(); test_credentials_store_duplicate_rejected(); test_credentials_store_rejects_nonuniform_iters(); test_credentials_store_parse_missing_file();