/* See LICENSE file for copyright and license details. */
#include "common.h"
#ifdef SUPPORT_BLAKE2B
#define BLAKE2B_BLOCK_SIZE 128U
LIBHASHSUM_1_NONNULL_
static size_t
process(struct libhashsum_hasher *this, const void *data, size_t bytes)
{
if (this->state.blake2b.keybytes && bytes) {
libblake_blake2b_force_update(&this->state.blake2b.s, this->state.blake2b.buf, BLAKE2B_BLOCK_SIZE);
this->state.blake2b.keybytes = 0;
memset(this->state.blake2b.buf, 0, (size_t)this->state.blake2b.keybytes);
}
return libblake_blake2b_update(&this->state.blake2b.s, data, bytes);
}
LIBHASHSUM_1_NONNULL_
static int
finalise_const(struct libhashsum_hasher *this, const void *data, size_t bytes, unsigned extra_bits)
{
const unsigned char *m = data;
size_t r;
if (extra_bits) {
errno = EINVAL;
return -1;
}
r = process(this, data, bytes);
m = &m[r];
bytes -= r;
if (!this->hash_output)
this->hash_output = this->state.blake2b.buf;
if (this->state.blake2b.keybytes)
bytes = BLAKE2B_BLOCK_SIZE;
else
memcpy(this->state.blake2b.buf, m, bytes);
libblake_blake2b_digest(&this->state.blake2b.s, this->state.blake2b.buf,
bytes, 0, this->hash_size, this->hash_output);
memset(&this->state.blake2b.s, 0, sizeof(this->state.blake2b.s));
this->state.blake2b.keybytes = 0;
return 0;
}
LIBHASHSUM_1_NONNULL_
static int
finalise(struct libhashsum_hasher *this, void *data, size_t bytes, unsigned extra_bits, size_t size)
{
unsigned char *m = data;
size_t r;
if (extra_bits) {
errno = EINVAL;
return -1;
}
r = process(this, data, bytes);
m = &m[r];
bytes -= r;
size -= r;
if (!this->hash_output)
this->hash_output = this->state.blake2b.buf;
if (this->state.blake2b.keybytes) {
bytes = BLAKE2B_BLOCK_SIZE;
m = this->state.blake2b.buf;
} else if (size < libblake_blake2b_digest_get_required_input_size(bytes)) {
memcpy(this->state.blake2b.buf, m, bytes);
m = this->state.blake2b.buf;
}
libblake_blake2b_digest(&this->state.blake2b.s, m, bytes, 0, this->hash_size, this->hash_output);
memset(&this->state.blake2b.s, 0, sizeof(this->state.blake2b.s));
this->state.blake2b.keybytes = 0;
return 0;
}
#if defined(__GNUC__)
__attribute__((__pure__))
#endif
static int
allzeroes(const uint8_t *data, size_t n)
{
while (n--)
if (data[n])
return 0;
return 1;
}
static char *
hex(char *restrict buf, const uint8_t *restrict data, size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
*buf++ = "0123456789abcdef"[(data[i] >> 4) & 15];
*buf++ = "0123456789abcdef"[(data[i] >> 0) & 15];
}
return buf;
}
static const char *
mkalgostr(char *buf, const char *name, size_t hashbits, const uint8_t *salt,
const uint8_t *pepper, size_t salt_pepper_bytes, const uint8_t *key, size_t keybytes)
{
char *p, *b;
int with_salt = (salt && !allzeroes(salt, salt_pepper_bytes));
int with_pepper = (pepper && !allzeroes(pepper, salt_pepper_bytes));
if (!hashbits && !with_salt && !with_pepper && !keybytes)
return name;
b = p = stpcpy(buf, name);
if (hashbits)
p = &p[sprintf(p, ",n=%zu", hashbits)];
if (with_salt)
p = hex(stpcpy(p, ",salt="), salt, salt_pepper_bytes);
if (with_pepper)
p = hex(stpcpy(p, ",pepper="), pepper, salt_pepper_bytes);
if (keybytes)
p = hex(stpcpy(p, ",key="), key, keybytes);
*b = '[';
*p++ = ']';
*p++ = '\0';
return buf;
}
int
libhashsum_init_blake2b_hasher(struct libhashsum_hasher *this, size_t hashbits, const void *salt,
const void *pepper, const void *key, size_t keybits)
{
struct libblake_blake2b_params params = {
.digest_len = (unsigned char)(hashbits / 8U),
.key_len = (unsigned char)(keybits / 8U),
.fanout = 1U,
.depth = 1U,
.leaf_len = 0U,
.node_offset = 0U,
.node_depth = 0U,
.inner_len = 0U
};
if (((hashbits | keybits) & 7U) || hashbits > 512U || keybits > 512U) {
errno = EINVAL;
return -1;
}
if (!hashbits) {
hashbits = 512U;
params.digest_len = (unsigned char)(hashbits / 8U);
}
libblake_init();
this->algorithm = LIBHASHSUM_BLAKE2B;
this->algorithm_string = mkalgostr(this->state.blake2b.algostr, "BLAKE2b",
hashbits == 512U ? 0U : hashbits,
salt, pepper, 16U, key, keybits / 8U);
this->input_block_size = BLAKE2B_BLOCK_SIZE;
this->hash_size = hashbits / 8U;
this->hash_output = NULL;
this->supports_non_whole_bytes = 0;
this->standard_partial_byte_input_encoding = LIBHASHSUM_UNSUPPORTED;
this->standard_partial_byte_output_encoding = LIBHASHSUM_UNSUPPORTED;
this->hash_excess_bits = 0;
this->process = &process;
this->finalise_const = &finalise_const;
this->finalise = &finalise;
this->stretch = NULL;
this->destroy = NULL;
if (salt)
memcpy(params.salt, salt, sizeof(params.salt));
if (pepper)
memcpy(params.pepper, pepper, sizeof(params.pepper));
this->state.blake2b.keybytes = (unsigned char)(keybits / 8U);
if (this->state.blake2b.keybytes) {
memcpy(this->state.blake2b.buf, key, keybits / 8U);
memset(&this->state.blake2b.buf[keybits / 8U], 0, sizeof(this->state.blake2b.buf) - keybits / 8U);
}
libblake_blake2b_init(&this->state.blake2b.s, ¶ms);
return 0;
}
#else
int
libhashsum_init_blake2b_hasher(struct libhashsum_hasher *this, size_t hashbits, const void *salt,
const void *pepper, const void *key, size_t keybits)
{
(void) this;
(void) hashbits;
(void) salt;
(void) pepper;
(void) key;
(void) keybits;
errno = ENOSYS;
return -1;
}
#endif