/* See LICENSE file for copyright and license details. */ #include "common.h" #ifdef LIBHASHSUM_INCLUDE_LIBKECCAK_STATE LIBHASHSUM_1_NONNULL_ static void stretch(struct libhashsum_hasher *this, int skip, void *buffer) { if (skip) { libkeccak_fast_squeeze(&this->state.keccak.s, 1); return; } if (buffer) this->hash_output = NULL; else if (this->hash_size > sizeof(this->state.keccak.sum.buf)) this->hash_output = this->state.keccak.sum.dyn; else this->hash_output = this->state.keccak.sum.buf; libkeccak_squeeze(&this->state.keccak.s, buffer ? buffer : this->hash_output); } LIBHASHSUM_1_NONNULL_ static size_t process(struct libhashsum_hasher *this, const void *data, size_t bytes) { bytes -= bytes % this->input_block_size; libkeccak_zerocopy_update(&this->state.keccak.s, data, bytes); return bytes; } LIBHASHSUM_1_NONNULL_ static void finalise_common(struct libhashsum_hasher *this) { if (this->state.keccak.squeezes > 2U) { size_t squeezes = this->state.keccak.squeezes - 2U; while (squeezes > (size_t)LONG_MAX) { libkeccak_fast_squeeze(&this->state.keccak.s, LONG_MAX); squeezes -= (size_t)LONG_MAX; } libkeccak_fast_squeeze(&this->state.keccak.s, (long int)squeezes); } if (this->state.keccak.squeezes > 1U) libkeccak_squeeze(&this->state.keccak.s, this->hash_output); libkeccak_state_wipe_message(&this->state.keccak.s); this->stretch = &stretch; } LIBHASHSUM_1_NONNULL_ static int finalise_const(struct libhashsum_hasher *this, const void *data, size_t bytes, unsigned extra_bits) { const uint8_t *m = data; size_t r; if (extra_bits > 7U) { errno = EINVAL; return -1; } r = process(this, m, bytes); m = &m[r]; bytes -= r; if (!this->hash_output) { if (this->hash_size > sizeof(this->state.keccak.sum.buf)) this->hash_output = this->state.keccak.sum.dyn; else this->hash_output = this->state.keccak.sum.buf; } libkeccak_digest(&this->state.keccak.s, m, bytes, extra_bits, this->state.keccak.suffix, this->state.keccak.squeezes > 1U ? NULL : this->hash_output); finalise_common(this); return 0; } LIBHASHSUM_1_NONNULL_ static int finalise(struct libhashsum_hasher *this, void *data, size_t bytes, unsigned extra_bits, size_t size) { uint8_t *m = data; size_t r; size_t need; if (extra_bits > 7U) { errno = EINVAL; return -1; } r = process(this, m, bytes); m = &m[r]; bytes -= r; size -= r; need = strlen(this->state.keccak.suffix) + 2U + extra_bits; need = (need + 7U) >> 3; need += bytes; if (need & (this->input_block_size - 1U)) { need &= ~(this->input_block_size - 1U); need += this->input_block_size; } if (!this->hash_output) { if (this->hash_size > sizeof(this->state.keccak.sum.buf)) this->hash_output = this->state.keccak.sum.dyn; else this->hash_output = this->state.keccak.sum.buf; } if (size < need) libkeccak_digest(&this->state.keccak.s, m, bytes, extra_bits, this->state.keccak.suffix, this->state.keccak.squeezes > 1U ? NULL : this->hash_output); else libkeccak_zerocopy_digest(&this->state.keccak.s, m, bytes, extra_bits, this->state.keccak.suffix, this->state.keccak.squeezes > 1U ? NULL : this->hash_output); finalise_common(this); return 0; } LIBHASHSUM_1_NONNULL_ static void destroy(struct libhashsum_hasher *this) { libkeccak_state_wipe_message(&this->state.keccak.s); libkeccak_state_fast_destroy(&this->state.keccak.s); memset(&this->state.keccak.s, 0, sizeof(this->state.keccak.s)); this->state.keccak.s.M = NULL; if (this->hash_size > sizeof(this->state.keccak.sum.buf)) { free(this->state.keccak.sum.dyn); this->state.keccak.sum.dyn = NULL; } } static uint64_t estimate_performance(long int r, long int c) { uint64_t base = 22517998136852480ULL / (uint64_t)r; switch (r + c) { case 1600: return base / 368ULL; case 800: return base / 2042ULL; case 400: return base / 1858ULL; case 200: return base / 1747ULL; default: return 0; } } int libhashsum_init_keccak__(struct libhashsum_hasher *this, size_t hashbits, void *spec_, size_t squeezes, const char *suffix) { struct libkeccak_spec *spec = spec_; this->hash_size = hashbits >> 3; this->hash_size += (size_t)!!(hashbits & 7U); this->hash_output = NULL; this->supports_non_whole_bytes = 1; this->standard_partial_byte_input_encoding = LIBHASHSUM_LEAST_SIGNIFICANT; this->standard_partial_byte_output_encoding = LIBHASHSUM_LEAST_SIGNIFICANT; this->hash_excess_bits = (8U - (hashbits & 7U)) & 7U; this->state.keccak.squeezes = squeezes; this->state.keccak.suffix = suffix; if (this->hash_size > sizeof(this->state.keccak.sum.buf)) { this->state.keccak.sum.dyn = malloc(this->hash_size); if (!this->state.keccak.sum.dyn) return -1; } if (libkeccak_state_initialise(&this->state.keccak.s, spec)) { if (this->hash_size > sizeof(this->state.keccak.sum.buf)) { free(this->state.keccak.sum.dyn); this->state.keccak.sum.dyn = NULL; } return -1; } this->input_block_size = libkeccak_zerocopy_chunksize(&this->state.keccak.s); this->relative_performance = estimate_performance(spec->bitrate, spec->capacity); this->process = &process; this->finalise_const = &finalise_const; this->finalise = &finalise; this->stretch = NULL; this->destroy = &destroy; return 0; } #else int libhashsum_init_keccak__(struct libhashsum_hasher *this, size_t hashbits, void *spec, size_t squeezes, const char *suffix); { (void) this; (void) spec; (void) squeezes; (void) suffix; errno = ENOSYS; return -1; } #endif