#include "bee2/core/mem.h"
#include "bee2/core/util.h"
#include "bee2/crypto/bash.h"
#define BASH_PRG_NULL 0x01
#define BASH_PRG_KEY 0x05
#define BASH_PRG_DATA 0x09
#define BASH_PRG_TEXT 0x0D
#define BASH_PRG_OUT 0x11
typedef struct {
size_t l;
size_t d;
octet s[192];
size_t buf_len;
size_t pos;
octet t[192];
octet stack[];
} bash_prg_st;
size_t bashPrg_keep()
{
return sizeof(bash_prg_st) + bashF_deep();
}
#define bashPrgIsKeymode(state)\
(16 * (192 - ((const bash_prg_st*)state)->buf_len) == \
((const bash_prg_st*)state)->l * (2 + ((const bash_prg_st*)state)->d))
static void bashPrgCommit(octet code, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsValid(st, bashPrg_keep()));
ASSERT(st->pos < st->buf_len);
st->s[st->pos] ^= code;
st->s[st->buf_len] ^= 0x80;
bashF(st->s, st->stack);
st->pos = 0;
}
void bashPrgStart(void* state, size_t l, size_t d, const octet ann[],
size_t ann_len, const octet key[], size_t key_len)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsValid(st, bashPrg_keep()));
ASSERT(l == 128 || l == 192 || l == 256);
ASSERT(d == 1 || d == 2);
ASSERT(ann_len % 4 == 0 && ann_len <= 60);
ASSERT(key_len % 4 == 0 && key_len <= 60);
ASSERT(key_len == 0 || key_len >= l / 8);
ASSERT(memIsDisjoint2(st, bashPrg_keep(), ann, ann_len));
ASSERT(memIsDisjoint2(st, bashPrg_keep(), key, key_len));
st->pos = 1 + ann_len + key_len;
st->s[0] = (octet)(ann_len * 4 + key_len / 4);
memCopy(st->s + 1, ann, ann_len);
memCopy(st->s + 1 + ann_len, key, key_len);
memSetZero(st->s + st->pos, 192 - st->pos);
st->s[192 - 8] = (octet)(l / 4 + d);
st->buf_len = key_len ? (192 - l * (2 + d) / 16) : (192 - d * l / 4);
st->l = l, st->d = d;
}
void bashPrgRestart(const octet ann[], size_t ann_len,
const octet key[], size_t key_len, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsValid(st, bashPrg_keep()));
ASSERT(ann_len % 4 == 0 && ann_len <= 60);
ASSERT(key_len % 4 == 0 && key_len <= 60);
ASSERT(key_len == 0 || key_len >= st->l / 8);
ASSERT(memIsDisjoint2(st, bashPrg_keep(), ann, ann_len));
ASSERT(memIsDisjoint2(st, bashPrg_keep(), key, key_len));
if (key_len)
{
bashPrgCommit(BASH_PRG_KEY, state);
st->buf_len = 192 - st->l * (2 + st->d) / 16;
}
else
bashPrgCommit(BASH_PRG_NULL, state);
st->pos = 1 + ann_len + key_len;
st->s[0] ^= (octet)(ann_len * 4 + key_len / 4);
memXor2(st->s + 1, ann, ann_len);
memXor2(st->s + 1 + ann_len, key, key_len);
}
void bashPrgAbsorbStart(void* state)
{
bashPrgCommit(BASH_PRG_DATA, state);
}
void bashPrgAbsorbStep(const void* buf, size_t count, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsDisjoint2(st, bashPrg_keep(), buf, count));
if (count < st->buf_len - st->pos)
{
memXor2(st->s + st->pos, buf, count);
st->pos += count;
return;
}
memXor2(st->s + st->pos, buf, st->buf_len - st->pos);
buf = (const octet*)buf + st->buf_len - st->pos;
count -= st->buf_len - st->pos;
bashF(st->s, st->stack);
while (count >= st->buf_len)
{
memXor2(st->s, buf, st->buf_len);
buf = (const octet*)buf + st->buf_len;
count -= st->buf_len;
bashF(st->s, st->stack);
}
if (st->pos = count)
memXor2(st->s, buf, count);
}
void bashPrgAbsorb(const void* buf, size_t count, void* state)
{
bashPrgAbsorbStart(state);
bashPrgAbsorbStep(buf, count, state);
}
void bashPrgSqueezeStart(void* state)
{
bashPrgCommit(BASH_PRG_OUT, state);
}
void bashPrgSqueezeStep(void* buf, size_t count, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsDisjoint2(st, bashPrg_keep(), buf, count));
if (count < st->buf_len - st->pos)
{
memCopy(buf, st->s + st->pos, count);
st->pos += count;
return;
}
memCopy(buf, st->s + st->pos, st->buf_len - st->pos);
buf = (octet*)buf + st->buf_len - st->pos;
count -= st->buf_len - st->pos;
bashF(st->s, st->stack);
while (count >= st->buf_len)
{
memCopy(buf, st->s, st->buf_len);
buf = (octet*)buf + st->buf_len;
count -= st->buf_len;
bashF(st->s, st->stack);
}
if (st->pos = count)
memCopy(buf, st->s, count);
}
void bashPrgSqueeze(void* buf, size_t count, void* state)
{
bashPrgSqueezeStart(state);
bashPrgSqueezeStep(buf, count, state);
}
void bashPrgEncrStart(void* state)
{
ASSERT(bashPrgIsKeymode(state));
bashPrgCommit(BASH_PRG_TEXT, state);
}
void bashPrgEncrStep(void* buf, size_t count, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsDisjoint2(st, bashPrg_keep(), buf, count));
if (count < st->buf_len - st->pos)
{
memXor2(st->s + st->pos, buf, count);
memCopy(buf, st->s + st->pos, count);
st->pos += count;
return;
}
memXor2(st->s + st->pos, buf, st->buf_len - st->pos);
memCopy(buf, st->s + st->pos, st->buf_len - st->pos);
buf = (octet*)buf + st->buf_len - st->pos;
count -= st->buf_len - st->pos;
bashF(st->s, st->stack);
while (count >= st->buf_len)
{
memXor2(st->s, buf, st->buf_len);
memCopy(buf, st->s, st->buf_len);
buf = (octet*)buf + st->buf_len;
count -= st->buf_len;
bashF(st->s, st->stack);
}
if (st->pos = count)
{
memXor2(st->s, buf, count);
memCopy(buf, st->s, count);
}
}
void bashPrgEncr(void* buf, size_t count, void* state)
{
bashPrgEncrStart(state);
bashPrgEncrStep(buf, count, state);
}
void bashPrgDecrStart(void* state)
{
ASSERT(bashPrgIsKeymode(state));
bashPrgCommit(BASH_PRG_TEXT, state);
}
void bashPrgDecrStep(void* buf, size_t count, void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsDisjoint2(st, bashPrg_keep(), buf, count));
if (count < st->buf_len - st->pos)
{
memXor2(buf, st->s + st->pos, count);
memXor2(st->s + st->pos, buf, count);
st->pos += count;
return;
}
memXor2(buf, st->s + st->pos, st->buf_len - st->pos);
memXor2(st->s + st->pos, buf, st->buf_len - st->pos);
buf = (octet*)buf + st->buf_len - st->pos;
count -= st->buf_len - st->pos;
bashF(st->s, st->stack);
while (count >= st->buf_len)
{
memXor2(buf, st->s, st->buf_len);
memXor2(st->s, buf, st->buf_len);
buf = (octet*)buf + st->buf_len;
count -= st->buf_len;
bashF(st->s, st->stack);
}
if (st->pos = count)
{
memXor2(buf, st->s, count);
memXor2(st->s, buf, count);
}
}
void bashPrgDecr(void* buf, size_t count, void* state)
{
bashPrgDecrStart(state);
bashPrgDecrStep(buf, count, state);
}
void bashPrgRatchet(void* state)
{
bash_prg_st* st = (bash_prg_st*)state;
ASSERT(memIsValid(st, bashPrg_keep()));
memCopy(st->t, st->s, 192);
bashPrgCommit(BASH_PRG_NULL, state);
memXor2(st->s, st->t, 192);
}