/* Test that the prefix argument to crypt_gensalt affects only the choice of hashing method, not any of the parameters or the salt. Written by Zack Weinberg in 2018. To the extent possible under law, Zack Weinberg has waived all copyright and related or neighboring rights to this work. See https://creativecommons.org/publicdomain/zero/1.0/ for further details. */ #include "crypt-port.h" #include #include /* Random bytes used when calling crypt_gensalt; for determinism, these are fixed from run to run. */ #define N_RBYTES 64ul static const char rbytes1[] = "90idUkI2+mu2E/tMTViD418j2sPdEYq9LYq0yRW7RYhr4RqQ+oVzIIEcfJBqpf/D"; static const char rbytes2[] = "sEwXQxrjBTEADauxCpvOQqq7iU9oq6uJ+Iux/fbhtLRj1MWgBFyo/t+nh/nzm0Kn"; static_assert(sizeof rbytes1 == N_RBYTES + 1, "rbytes1 is wrong length"); static_assert(sizeof rbytes2 == N_RBYTES + 1, "rbytes2 is wrong length"); struct testcase { const char *prefix; unsigned long count1; unsigned long count2; }; /* This list should include one entry for each potentially-supported hash prefix. If the hash method has tunable cost, set count1 and count2 to two different nonzero values, within the supported cost range. Neither value should equal the default cost. If the hash method does not have tunable cost, set count1 and count2 to zero. */ static const struct testcase testcases[] = { #if INCLUDE_descrypt || INCLUDE_bigcrypt { "", 0, 0 }, #endif #if INCLUDE_bsdicrypt { "_", 7019, 1120211 }, #endif #if INCLUDE_nt { "$3$", 0, 0 }, #endif #if INCLUDE_md5crypt { "$1$", 0, 0 }, #endif #if INCLUDE_sunmd5 { "$md5", 7019, 1120211 }, #endif #if INCLUDE_sha1crypt { "$sha1", 7019, 1120211 }, #endif #if INCLUDE_sha256crypt { "$5$", 7019, 1120211 }, #endif #if INCLUDE_sha512crypt { "$6$", 7019, 1120211 }, #endif #if INCLUDE_bcrypt { "$2b$", 7, 11 }, #endif #if INCLUDE_bcrypt_y { "$2y$", 7, 11 }, #endif #if INCLUDE_bcrypt_a { "$2a$", 7, 11 }, #endif #if INCLUDE_scrypt { "$7$", 7, 11, }, #endif #if INCLUDE_yescrypt { "$y$", 7, 11, }, #endif #if INCLUDE_gost_yescrypt { "$gy$", 7, 11, }, #endif { 0, 0, 0, } }; static int do_crypt_gensalt(const char *prefix, const char rbytes[MIN_SIZE(N_RBYTES)], unsigned long count, char outbuf[MIN_SIZE(CRYPT_GENSALT_OUTPUT_SIZE)]) { /* Detect failure to NUL-terminate the output properly. */ static int ncalls = 0; memset(outbuf, '!' + (ncalls % ('~' - '!' + 1)), CRYPT_GENSALT_OUTPUT_SIZE - 1); outbuf[CRYPT_GENSALT_OUTPUT_SIZE - 1] = 0; ncalls++; char *rv = crypt_gensalt_rn(prefix, count, rbytes, N_RBYTES, outbuf, CRYPT_GENSALT_OUTPUT_SIZE); if (rv == 0) { printf("ERROR: gensalt(%s, %lu, %c%c..., %lu, outbuf, %lu) = 0/%s\n", prefix, count, rbytes[0], rbytes[1], N_RBYTES, (unsigned long)CRYPT_GENSALT_OUTPUT_SIZE, strerror(errno)); outbuf[0] = '*'; memset (outbuf+1, 0, CRYPT_GENSALT_OUTPUT_SIZE-1); return 1; } else if (rv[0] == '*') { printf("ERROR: gensalt(%s, %lu, %c%c..., %lu, outbuf, %lu) = %s/%s\n", prefix, count, rbytes[0], rbytes[1], N_RBYTES, (unsigned long)CRYPT_GENSALT_OUTPUT_SIZE, outbuf, strerror(errno)); outbuf[0] = '*'; memset (outbuf+1, 0, CRYPT_GENSALT_OUTPUT_SIZE-1); return 1; } else return 0; } static int do_check_equal(const char *stst, const char *sref, const char *prefix, const char rbytes[N_RBYTES], unsigned long count, const char *setting) { if (!strcmp(stst, sref)) return 0; printf("FAIL: expected %s\n" " got %s\n" " from %s, %lu, %c%c...\n" " and %s\n", sref, stst, prefix, count, rbytes[0], rbytes[1], setting); return 1; } int main(void) { int status = 0; char sref[6][CRYPT_GENSALT_OUTPUT_SIZE]; char stst[CRYPT_GENSALT_OUTPUT_SIZE]; for (size_t i = 0; testcases[i].prefix; i++) { const char *prefix = testcases[i].prefix; unsigned long count1 = testcases[i].count1; unsigned long count2 = testcases[i].count2; int ncases; memset(sref, 0, sizeof sref); /* If count1 and count2 are both nonzero, then they should also be unequal, and we have six reference cases: (0, count1, count2) x (rbytes1, rbytes2). If count1 and count2 are both zero, then we only have two reference cases: 0 x (rbytes1, rbytes2) (this happens when the hash method doesn't have tunable cost). It is incorrect for only one of count1 and count2 to be zero, or for them to be equal but nonzero. */ if (count1 == 0 && count2 == 0) { ncases = 2; status |= do_crypt_gensalt(prefix, rbytes1, 0, sref[0]); status |= do_crypt_gensalt(prefix, rbytes2, 0, sref[1]); } else if (count1 != 0 && count2 != 0 && count1 != count2) { ncases = 6; status |= do_crypt_gensalt(prefix, rbytes1, 0, sref[0]); status |= do_crypt_gensalt(prefix, rbytes2, 0, sref[1]); status |= do_crypt_gensalt(prefix, rbytes1, count1, sref[2]); status |= do_crypt_gensalt(prefix, rbytes2, count1, sref[3]); status |= do_crypt_gensalt(prefix, rbytes1, count2, sref[4]); status |= do_crypt_gensalt(prefix, rbytes2, count2, sref[5]); } else { printf ("ERROR: %zu/%s: inappropriate count1=%lu count2=%lu\n", i, prefix, count1, count2); status = 1; continue; } /* At this point, sref[0..ncases] are filled with setting strings corresponding to different combinations of salt and cost. If we reuse those strings as prefixes for crypt_gensalt, none of the additional information should affect the output. */ for (int j = 0; j < ncases; j++) { if (sref[j][0] == '*') continue; /* initial crypt_gensalt call failed */ if (count1 == 0 && count2 == 0) { status |= do_crypt_gensalt(sref[j], rbytes1, 0, stst); status |= do_check_equal(stst, sref[0], prefix, rbytes1, 0, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes2, 0, stst); status |= do_check_equal(stst, sref[1], prefix, rbytes2, 0, sref[j]); } else { status |= do_crypt_gensalt(sref[j], rbytes1, 0, stst); status |= do_check_equal(stst, sref[0], prefix, rbytes1, 0, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes2, 0, stst); status |= do_check_equal(stst, sref[1], prefix, rbytes2, 0, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes1, count1, stst); status |= do_check_equal(stst, sref[2], prefix, rbytes1, count1, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes2, count1, stst); status |= do_check_equal(stst, sref[3], prefix, rbytes2, count1, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes1, count2, stst); status |= do_check_equal(stst, sref[4], prefix, rbytes1, count2, sref[j]); status |= do_crypt_gensalt(sref[j], rbytes2, count2, stst); status |= do_check_equal(stst, sref[5], prefix, rbytes2, count2, sref[j]); } } } return status; }