/* 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 <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#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