#include <stdio.h>
/* See LICENSE file for copyright and license details. */
#include "common.h"
/**
* Encode a number in big-endian with a size prefix
*
* @param state The hashing state the feed the number to
* @param buf Buffer that is at least `byterate` bytes large
* @param byterate The byterate of the hashing algorithm
* @param value The number to send to the hashing sponge
* @param off Current offset in `buf`
* @return New offset in `buf`
*/
static size_t
encode_left(struct libkeccak_state *restrict state, uint8_t *restrict buf, size_t byterate, size_t value, size_t off)
{
size_t x, n, j, i = off;
for (x = value, n = 0; x; x >>= 8)
n += 1;
if (!n)
n = 1;
buf[i++] = (uint8_t)n;
if (i == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
i = 0;
}
for (j = 0; j < n;) {
buf[i++] = (uint8_t)(value >> ((n - ++j) << 3));
if (i == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
i = 0;
}
}
return i;
}
/**
* Encode a number in big-endian with a size prefix
*
* @param state The hashing state the feed the number to
* @param buf Buffer that is at least `byterate` bytes large
* @param byterate The byterate of the hashing algorithm
* @param value The number to send to the hashing sponge
* @param off Current offset in `buf`
* @param bitoff The number of bits to shift the encoded message left
* @return New offset in `buf`
*/
static size_t
encode_left_shifted(struct libkeccak_state *restrict state, uint8_t *restrict buf,
size_t byterate, size_t value, size_t off, size_t bitoff)
{
size_t x, n, j, i = off;
uint16_t v;
for (x = value, n = 0; x; x >>= 8)
n += 1;
if (!n)
n = 1;
v = (uint16_t)((n & 255UL) << bitoff);
buf[i++] |= (uint8_t)v;
if (i == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
i = 0;
}
buf[i] = (uint8_t)(n >> 8);
for (j = 0; j < n;) {
v = (uint16_t)(((value >> ((n - ++j) << 3)) & 255UL) << bitoff);
buf[i++] |= (uint8_t)v;
if (i == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
i = 0;
}
buf[i] = (uint8_t)(v >> 8);
}
return i;
}
/**
* Feed text to the sponge
*
* @param state The hashing state tofeed
* @param buf Buffer that is at least `byterate` bytes large
* @param text Text to feed the sponge
* @param bytes The number of whole bytes in `text`
* @param bits The number of bits at the end of `text`
* that does not make up a whole byte
* @param suffix Bit-string (encoded with ASCII 0/1 digits)
* of additional bytes after `text`
* @param off The current byte offset in `buf`
* @param byterate The byterate of the hashing algorithm
* @param bitoffp Output parameter for the non-whole
* byte bit-offset (current must be 0)
* @return The new byte offset in `buf`
*/
static size_t
feed_text(struct libkeccak_state *restrict state, uint8_t *restrict buf, const uint8_t *restrict text, size_t bytes,
size_t bits, const char *restrict suffix, size_t off, size_t byterate, size_t *restrict bitoffp)
{
size_t n, bitoff;
if (off) {
n = bytes < byterate - off ? bytes : byterate - off;
memcpy(&buf[off], text, n);
off += n;
if (off == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
off = 0;
}
text = &text[n];
bytes -= n;
}
if (bytes) {
n = bytes;
n -= bytes %= byterate;
libkeccak_zerocopy_update(state, text, n);
text = &text[n];
}
memcpy(&buf[off], text, bytes + !!bits);
off += bytes;
bitoff = bits;
if (!bitoff)
buf[off] = 0;
for (; *suffix; suffix++) {
if (*suffix == '1')
buf[off] |= (uint8_t)(1 << bitoff);
if (++bitoff == 8) {
if (++off == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
off = 0;
}
bitoff = 0;
buf[off] = 0;
}
}
*bitoffp = bitoff;
return off;
}
/**
* Feed text to the sponge
*
* @param state The hashing state tofeed
* @param buf Buffer that is at least `byterate` bytes large
* @param text Text to feed the sponge
* @param bytes The number of whole bytes in `text`
* @param bits The number of bits at the end of `text`
* that does not make up a whole byte
* @param suffix Bit-string (encoded with ASCII 0/1 digits)
* of additional bytes after `text`
* @param off The current byte offset in `buf`
* @param byterate The byterate of the hashing algorithm
* @param bitoffp Pointer to the non-whole byte bit-offset,
* shall be the current (non-zero) bit-offset
* upon entry and will be set to the new
* bit-offset on return
* @return The new byte offset in `buf`
*/
static size_t
feed_text_shifted(struct libkeccak_state *restrict state, uint8_t *restrict buf, const uint8_t *restrict text, size_t bytes,
size_t bits, const char *restrict suffix, size_t off, size_t byterate, size_t *restrict bitoffp)
{
size_t i, bitoff = *bitoffp;
uint16_t v;
for (i = 0; i < bytes; i++) {
v = (uint16_t)((uint16_t)text[i] << bitoff);
buf[off] |= (uint8_t)v;
if (++off == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
off = 0;
}
buf[off] = (uint8_t)(v >> 8);
}
if (bits) {
v = (uint16_t)((uint16_t)text[bytes] << bitoff);
buf[off] |= (uint8_t)v;
bitoff += bits;
if (bitoff >= 8) {
if (++off == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
off = 0;
}
bitoff &= 7;
buf[off] = (uint8_t)(v >> 8);
}
}
if (!bitoff)
buf[off] = 0;
for (; *suffix; suffix++) {
if (*suffix == '1')
buf[off] |= (uint8_t)(1 << bitoff);
if (++bitoff == 8) {
if (++off == byterate) {
libkeccak_zerocopy_update(state, buf, byterate);
off = 0;
}
bitoff = 0;
buf[off] = 0;
}
}
*bitoffp = bitoff;
return off;
}
/**
* Create and absorb the initialisation blocks for cSHAKE hashing
*
* @param state The hashing state
* @param n_text Function name-string
* @param n_len Byte-length of `n_text` (only whole byte)
* @param n_bits Bit-length of `n_text`, minus `n_len * 8`
* @param n_suffix Bit-string, represented by a NUL-terminated
* string of '1':s and '0's:, making up the part
* after `n_text` of the function-name bit-string;
* `NULL` is treated as the empty string
* @param s_text Customisation-string
* @param s_len Byte-length of `s_text` (only whole byte)
* @param s_bits Bit-length of `s_text`, minus `s_len * 8`
* @param s_suffix Bit-string, represented by a NUL-terminated
* string of '1':s and '0's:, making up the part
* after `s_text` of the customisation bit-string;
* `NULL` is treated as the empty string
*/
void
libkeccak_cshake_initialise(struct libkeccak_state *restrict state,
const void *n_text, size_t n_len, size_t n_bits, const char *n_suffix,
const void *s_text, size_t s_len, size_t s_bits, const char *s_suffix)
{
size_t off = 0, bitoff, byterate = (size_t)state->r >> 3;
if (!n_suffix)
n_suffix = "";
if (!s_suffix)
s_suffix = "";
if (!n_len && !s_len && !n_bits && !s_bits && !*n_suffix && !*s_suffix)
return;
n_len += n_bits >> 3;
s_len += s_bits >> 3;
n_bits &= 7;
s_bits &= 7;
off = encode_left(state, state->M, byterate, byterate, off);
off = encode_left(state, state->M, byterate, (n_len << 3) + n_bits + strlen(n_suffix), off);
off = feed_text(state, state->M, n_text, n_len, n_bits, n_suffix, off, byterate, &bitoff);
if (!bitoff) {
off = encode_left(state, state->M, byterate, (s_len << 3) + s_bits + strlen(s_suffix), off);
off = feed_text(state, state->M, s_text, s_len, s_bits, s_suffix, off, byterate, &bitoff);
} else {
off = encode_left_shifted(state, state->M, byterate, (s_len << 3) + s_bits + strlen(s_suffix), off, bitoff);
off = feed_text_shifted(state, state->M, s_text, s_len, s_bits, s_suffix, off, byterate, &bitoff);
}
if (bitoff)
off++;
if (off) {
memset(&state->M[off], 0, byterate - off);
libkeccak_zerocopy_update(state, state->M, byterate);
}
}