#include "rivide/crypto/keccak.h"
#include "rivide/rivide_config.h"
#include "rivide/utils/mem.h"
static uint64_t rotl64(uint64_t x, unsigned int n) {
return n == 0 ? x : (x << n) | (x >> (64 - n));
}
static const uint64_t keccak_rc[24] = {
UINT64_C(0x0000000000000001), UINT64_C(0x0000000000008082), UINT64_C(0x800000000000808A),
UINT64_C(0x8000000080008000), UINT64_C(0x000000000000808B), UINT64_C(0x0000000080000001),
UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008009), UINT64_C(0x000000000000008A),
UINT64_C(0x0000000000000088), UINT64_C(0x0000000080008009), UINT64_C(0x000000008000000A),
UINT64_C(0x000000008000808B), UINT64_C(0x800000000000008B), UINT64_C(0x8000000000008089),
UINT64_C(0x8000000000008003), UINT64_C(0x8000000000008002), UINT64_C(0x8000000000000080),
UINT64_C(0x000000000000800A), UINT64_C(0x800000008000000A), UINT64_C(0x8000000080008081),
UINT64_C(0x8000000000008080), UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008008)};
static const unsigned int rho_offsets[25] = {0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43,
25, 39, 41, 45, 15, 21, 8, 18, 2, 61, 56, 14};
void rivide_keccak_f1600(uint64_t s[25]) {
int round;
uint64_t C[5], D[5], B[25];
int x, y;
for (round = 0; round < 24; round++) {
for (x = 0; x < 5; x++) {
C[x] = s[x] ^ s[x + 5] ^ s[x + 10] ^ s[x + 15] ^ s[x + 20];
}
for (x = 0; x < 5; x++) {
D[x] = C[(x + 4) % 5] ^ rotl64(C[(x + 1) % 5], 1);
}
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
s[5 * y + x] ^= D[x];
}
}
for (x = 0; x < 5; x++) {
for (y = 0; y < 5; y++) {
int idx = 5 * y + x;
B[5 * ((2 * x + 3 * y) % 5) + y] = rotl64(s[idx], rho_offsets[idx]);
}
}
for (y = 0; y < 5; y++) {
for (x = 0; x < 5; x++) {
s[5 * y + x] = B[5 * y + x] ^ (~B[5 * y + (x + 1) % 5] & B[5 * y + (x + 2) % 5]);
}
}
s[0] ^= keccak_rc[round];
}
}
void rivide_keccak_init(rivide_keccak_state_t *ctx, size_t rate) {
size_t i;
for (i = 0; i < 25; i++) {
ctx->state[i] = 0;
}
ctx->absorbed = 0;
ctx->rate = rate;
ctx->squeezing = 0;
}
void rivide_keccak_absorb(rivide_keccak_state_t *ctx, const uint8_t *in, size_t inlen) {
size_t rate = ctx->rate;
size_t absorbed = ctx->absorbed;
while (inlen > 0) {
size_t avail = rate - absorbed;
size_t chunk = (inlen < avail) ? inlen : avail;
if (in) {
size_t i;
for (i = 0; i < chunk; i++) {
size_t pos = absorbed + i;
size_t lane = pos / 8;
size_t offset = pos % 8;
ctx->state[lane] ^= (uint64_t)in[i] << (8 * offset);
}
}
absorbed += chunk;
if (in) {
in += chunk;
}
inlen -= chunk;
if (absorbed == rate) {
rivide_keccak_f1600(ctx->state);
absorbed = 0;
}
}
ctx->absorbed = absorbed;
}
void rivide_keccak_finalize(rivide_keccak_state_t *ctx, uint8_t domain_sep) {
size_t absorbed = ctx->absorbed;
size_t rate = ctx->rate;
size_t lane, offset;
lane = absorbed / 8;
offset = absorbed % 8;
ctx->state[lane] ^= (uint64_t)domain_sep << (8 * offset);
lane = (rate - 1) / 8;
offset = (rate - 1) % 8;
ctx->state[lane] ^= (uint64_t)0x80 << (8 * offset);
rivide_keccak_f1600(ctx->state);
ctx->absorbed = 0;
ctx->squeezing = 1;
}
void rivide_keccak_squeeze(rivide_keccak_state_t *ctx, uint8_t *out, size_t outlen) {
size_t rate = ctx->rate;
size_t offset = ctx->absorbed;
while (outlen > 0) {
if (offset == rate) {
rivide_keccak_f1600(ctx->state);
offset = 0;
}
size_t avail = rate - offset;
size_t chunk = (outlen < avail) ? outlen : avail;
{
size_t i;
for (i = 0; i < chunk; i++) {
size_t pos = offset + i;
size_t lane = pos / 8;
size_t shift = pos % 8;
out[i] = (uint8_t)(ctx->state[lane] >> (8 * shift));
}
}
offset += chunk;
out += chunk;
outlen -= chunk;
}
ctx->absorbed = offset;
}