/* See LICENSE file for copyright and license details. */ #if defined(__clang__) # pragma clang diagnostic ignored "-Wunsafe-buffer-usage" # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wcomma" # pragma clang diagnostic ignored "-Wcovered-switch-default" #endif #if defined(SUPPORT_SHA0) || defined(SUPPORT_SHA1) # define LIBHASHSUM_INCLUDE_LIBSHA1_STATE #endif #if defined(SUPPORT_SHA2) # define LIBHASHSUM_INCLUDE_LIBSHA2_STATE #endif #if defined(SUPPORT_KECCAK) || defined(SUPPORT_SHA3) || defined(SUPPORT_SHAKE) || defined(SUPPORT_RAWSHAKE) # define LIBHASHSUM_INCLUDE_LIBKECCAK_STATE #endif #if defined(SUPPORT_BLAKE224) || defined(SUPPORT_BLAKE256) # define SUPPORT_BLAKES #endif #if defined(SUPPORT_BLAKE384) || defined(SUPPORT_BLAKE512) # define SUPPORT_BLAKEB #endif #if defined(SUPPORT_BLAKES) || defined(SUPPORT_BLAKEB) # define SUPPORT_BLAKE #endif #if defined(SUPPORT_BLAKE2S) || defined(SUPPORT_BLAKE2B) # define SUPPORT_BLAKE2 #endif #if defined(SUPPORT_BLAKE) || defined(SUPPORT_BLAKE2) # define LIBHASHSUM_INCLUDE_LIBBLAKE_STATE #endif #include "libhashsum.h" #include #include #include #include #include #include #include #if defined(__GNUC__) __attribute__((__const__, __visibility__("hidden"))) #endif uint8_t libhashsum_reverse_byte__(uint8_t); #ifdef LIBHASHSUM_INCLUDE_LIBKECCAK_STATE #if defined(__GNUC__) __attribute__((__visibility__("hidden"))) #endif int libhashsum_init_keccak__(struct libhashsum_hasher *this, size_t hashbits, void *spec, size_t squeezes, const char *suffix); #endif #define KECCAKN(N) 1600 - 2 * (N), 2 * (N), (N), 1 #ifdef TEST struct testcase { size_t input_repeat; unsigned extra_bits; const char *input; const char *output; }; static void hex(char *restrict out, const unsigned char *restrict in, size_t n) { for (; n--; in++) { *out++ = "0123456789abcdef"[(*in >> 4) & 15]; *out++ = "0123456789abcdef"[(*in >> 0) & 15]; } *out = '\0'; } static char * escape(const char *s, size_t n) { char *ret, *p; ret = malloc(n * 4U + 1U); if (!ret) { perror("malloc"); exit(2); } for (p = ret; n--; s++) { if (*s == '"' || *s == '\\') { *p++ = '\\'; *p++ = *s; } else if (*s == '\n') { *p++ = '\\'; *p++ = 'n'; } else if (*s < ' ' || *s >= 127) { *p++ = '\\'; *p++ = 'x'; *p++ = "0123456789abcdef"[(*s >> 4) & 15]; *p++ = "0123456789abcdef"[(*s >> 0) & 15]; } else { *p++ = *s; } } *p = '\0'; return ret; } static int run_tests(const char *name, enum libhashsum_algorithm algorithm, size_t hash_size, struct testcase *testcases, size_t ntestcases, char hexsum[], unsigned char supports_non_whole_bytes) { struct libhashsum_hasher hasher, hasher2; unsigned char custom_hashbuffer[2048]; unsigned char custom_hashbuffer2[sizeof(custom_hashbuffer)]; int use_custom_hashbuffer = 0; char *input, *p, bitstr[sizeof(" + b\"1234567\"")]; unsigned char extra_bit; int ok = 1, caseok; size_t i, j, input_string_len, input_total_len, input_size; size_t bits; for (i = 0; i < ntestcases; i++, use_custom_hashbuffer ^= 1) { if (libhashsum_init_hasher(&hasher, algorithm)) { perror("libhashsum_init_hasher"); return 2; } if (hasher.algorithm != algorithm) { fprintf(stderr, "libhashsum_init_hasher returned unexpected value in .algorithm\n"); return 2; } if (!hasher.algorithm_string) { fprintf(stderr, "libhashsum_init_hasher returned NULL pointer in .algorithm_string\n"); return 2; } if (strcmp(hasher.algorithm_string, name)) { fprintf(stderr, "libhashsum_init_hasher returned unexpected value in .algorithm_string\n"); return 2; } if (hasher.hash_size != hash_size) { fprintf(stderr, "libhashsum_init_hasher returned unexpected value in .hash_size\n"); return 2; } if (hasher.hash_output) { fprintf(stderr, "libhashsum_init_hasher returned non-NULL pointer in .hash_output\n"); return 2; } if (!hasher.process) { fprintf(stderr, "libhashsum_init_hasher returned NULL pointer in .process\n"); return 2; } if (!hasher.finalise_const) { fprintf(stderr, "libhashsum_init_hasher returned NULL pointer in .finalise_const\n"); return 2; } if (!hasher.finalise) { fprintf(stderr, "libhashsum_init_hasher returned NULL pointer in .finalise\n"); return 2; } if (hasher.stretch) { fprintf(stderr, "libhashsum_init_hasher returned non-NULL pointer in .stretch\n"); return 2; } if (hasher.supports_non_whole_bytes != supports_non_whole_bytes) { fprintf(stderr, "libhashsum_init_hasher returned unexpected value in .supports_non_whole_bytes\n"); return 2; } input_string_len = strlen(testcases[i].input); bits = testcases[i].extra_bits; if (bits) { if (input_string_len < (bits + 7U) / 8U) input_string_len = (bits + 7U) / 8U; bits %= 8U; if (bits) input_string_len -= 1U; } input_total_len = testcases[i].input_repeat * input_string_len; input_size = input_total_len + hasher.input_block_size + 1U; p = input = malloc(input_size); if (!input) { perror("malloc"); return 2; } for (j = 0; j < testcases[i].input_repeat; j++) { memcpy(p, testcases[i].input, input_string_len); p = &p[input_string_len]; } if (bits) *p = testcases[i].input[input_string_len]; hasher.hash_output = use_custom_hashbuffer ? custom_hashbuffer : NULL; if (hasher.finalise_const(&hasher, input, input_total_len, (unsigned)bits)) { perror("hasher.finalise_const"); return 2; } if (!hasher.hash_output) { fprintf(stderr, "hasher.finalise_const did not set hasher.hash_output\n"); return 2; } #ifndef NO_CUSTOM_HASH_BUFFER_SUPPORT if (use_custom_hashbuffer && hasher.hash_output != custom_hashbuffer) { fprintf(stderr, "hasher.finalise_const did keep custom hash buffer\n"); return 2; } #endif hex(hexsum, hasher.hash_output, hasher.hash_size); ok &= caseok = !testcases[i].output || !strcmp(hexsum, testcases[i].output); if (libhashsum_init_hasher(&hasher2, algorithm)) { perror("libhashsum_init_hasher"); return 2; } hasher2.hash_output = use_custom_hashbuffer ? custom_hashbuffer2 : NULL; if (hasher2.finalise(&hasher2, input, input_total_len, (unsigned)bits, input_size)) { perror("hasher.finalise"); return 2; } if (!hasher2.hash_output) { fprintf(stderr, "hasher.finalise did not set hasher.hash_output\n"); return 2; } #ifndef NO_CUSTOM_HASH_BUFFER_SUPPORT if (use_custom_hashbuffer && hasher2.hash_output != custom_hashbuffer2) { fprintf(stderr, "hasher.finalise did keep custom hash buffer\n"); return 2; } #endif if (memcmp(hasher2.hash_output, hasher.hash_output, hasher.hash_size)) { fprintf(stderr, "hasher.finalise and hasher.finalise_const produced different hashes\n"); return 2; } free(input); input = escape(testcases[i].input, input_string_len); bitstr[0] = '\0'; if (bits) { extra_bit = (unsigned char)testcases[i].input[input_string_len]; p = bitstr; *p++ = ' '; *p++ = '+'; *p++ = ' '; *p++ = 'b'; *p++ = '"'; while (bits--) { *p++ = "01"[extra_bit & 1U]; extra_bit >>= 1; } *p++ = '"'; *p = '\0'; } if (testcases[i].input_repeat == 1) fprintf(stderr, "[\033[1;%s\033[m] %s(\"%s\"%s) = %s\n", caseok ? "32mPASS" : "31mFAIL", name, input, bitstr, hexsum); else if (!testcases[i].input_repeat && *bitstr) fprintf(stderr, "[\033[1;%s\033[m] %s(%s) = %s\n", caseok ? "32mPASS" : "31mFAIL", name, &bitstr[3], hexsum); else if (!testcases[i].input_repeat) fprintf(stderr, "[\033[1;%s\033[m] %s(\"\") = %s\n", caseok ? "32mPASS" : "31mFAIL", name, hexsum); else fprintf(stderr, "[\033[1;%s\033[m] %s(%zu * \"%s\"%s) = %s\n", caseok ? "32mPASS" : "31mFAIL", name, testcases[i].input_repeat, input, bitstr, hexsum); free(input); if (hasher.destroy) hasher.destroy(&hasher); } return !ok; } # define TEST_MAIN(NAME, ID, SUPPORTS_NON_WHOLE_BYTES)\ char hexsum[LIBHASHSUM_##ID##_HASH_SIZE * 2 + 1];\ return run_tests(NAME, LIBHASHSUM_##ID, LIBHASHSUM_##ID##_HASH_SIZE,\ testcases, sizeof(testcases) / sizeof(*testcases), hexsum,\ (SUPPORTS_NON_WHOLE_BYTES)) # define ASSERT(ASSERTION)\ do {\ if ((ASSERTION))\ break;\ fprintf(stderr, "assertion `%s` at line %i failed\n", #ASSERTION, __LINE__);\ exit(2);\ } while (0) #endif #ifdef TEST_UNSUPPORTED # define TEST_MAIN(NAME, ID)\ struct libhashsum_hasher hasher;\ if (!libhashsum_init_hasher(&hasher, LIBHASHSUM_##ID)) {\ fprintf(stderr, "expected libhashsum_init_hasher to fail, but it returned successfully\n");\ return 2;\ }\ if (errno != ENOSYS) {\ perror("expected libhashsum_init_hasher to set errno to ENOSYS, but got");\ return 2;\ }\ return 0; #endif