Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keccak init-update-final api #229

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions include/ethash/keccak.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,25 @@ union ethash_hash256 ethash_keccak256_32(const uint8_t data[32]) noexcept;
union ethash_hash512 ethash_keccak512(const uint8_t* data, size_t size) noexcept;
union ethash_hash512 ethash_keccak512_64(const uint8_t data[64]) noexcept;

struct ethash_keccak256_context {
size_t hash_size;
size_t block_size;
uint64_t state[25];
uint64_t* state_iter;
uint64_t last_word;
uint8_t* last_word_iter;
uint8_t buffer[136];
size_t buffer_index;
};

void ethash_keccak256_init(struct ethash_keccak256_context* ctx) noexcept;
void ethash_keccak256_update(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size) noexcept;
union ethash_hash256 ethash_keccak256_final(struct ethash_keccak256_context* ctx) noexcept;

void ethash_keccak256_init_2(struct ethash_keccak256_context* ctx) noexcept;
void ethash_keccak256_update_2(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size) noexcept;
union ethash_hash256 ethash_keccak256_final_2(struct ethash_keccak256_context* ctx) noexcept;

#ifdef __cplusplus
}
#endif
30 changes: 30 additions & 0 deletions include/ethash/keccak.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,34 @@ inline hash512 keccak512(const hash512& input) noexcept
static constexpr auto keccak256_32 = ethash_keccak256_32;
static constexpr auto keccak512_64 = ethash_keccak512_64;

inline void keccak256_init(struct ethash_keccak256_context* ctx) noexcept
{
ethash_keccak256_init(ctx);
}

inline void keccak256_update(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size) noexcept
{
ethash_keccak256_update(ctx, data, size);
}

inline hash256 keccak256_final(struct ethash_keccak256_context* ctx) noexcept
{
return ethash_keccak256_final(ctx);
}

inline void keccak256_init_2(struct ethash_keccak256_context* ctx) noexcept
{
ethash_keccak256_init_2(ctx);
}

inline void keccak256_update_2(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size) noexcept
{
ethash_keccak256_update_2(ctx, data, size);
}

inline hash256 keccak256_final_2(struct ethash_keccak256_context* ctx) noexcept
{
return ethash_keccak256_final_2(ctx);
}

} // namespace ethash
256 changes: 254 additions & 2 deletions lib/keccak/keccak.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#if !__has_builtin(__builtin_memcpy) && !defined(__GNUC__)
#include <string.h>
#define __builtin_memcpy memcpy
#define __builtin_memset memset
#endif

#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
Expand Down Expand Up @@ -44,7 +45,6 @@ static const uint64_t round_constants[24] = { //
0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a,
0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008};


/// The Keccak-f[1600] function.
///
/// The implementation of the Keccak-f function with 1600-bit width of the permutation (b).
Expand Down Expand Up @@ -342,11 +342,11 @@ static inline ALWAYS_INLINE void keccak(
++data;
--size;
}

*last_word_iter = 0x01;
*state_iter ^= to_le64(last_word);

state[(block_size / word_size) - 1] ^= 0x8000000000000000;

keccakf1600_best(state);

for (i = 0; i < (hash_size / word_size); ++i)
Expand Down Expand Up @@ -380,3 +380,255 @@ union ethash_hash512 ethash_keccak512_64(const uint8_t data[64])
keccak(hash.word64s, 512, data, 64);
return hash;
}

static inline ALWAYS_INLINE void keccak_init(struct ethash_keccak256_context* ctx, size_t bits)
{
__builtin_memset((uint8_t*)ctx->state, 0, sizeof ctx->state);
ctx->hash_size = bits / 8;
ctx->block_size = (1600 - bits * 2) / 8;

ctx->last_word = 0;
ctx->last_word_iter = (uint8_t*)&(ctx->last_word);

ctx->state_iter = ctx->state;
}

static inline ALWAYS_INLINE void keccak_update(
struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size)
{
static const size_t word_size = sizeof(uint64_t);
size_t i;

size_t block_size_b = ctx->block_size / word_size; // block size in bytes
size_t last_word_unfilled_size = // calculate unfilled space in last word
(word_size - (size_t)(ctx->last_word_iter - (uint8_t*)&(ctx->last_word)));
size_t state_unfilled_size = // calculate unfilled space in state
(block_size_b - (size_t)(ctx->state_iter - ctx->state));

// fill the last word unfilled space with bytes until it's full
while(last_word_unfilled_size > 0 && size > 0)
{
*ctx->last_word_iter = *data;
++ctx->last_word_iter;
++data;
--size;
--last_word_unfilled_size;
}

// if the last word is full, move it to state.
if(ctx->last_word_iter == (uint8_t*)&(ctx->last_word) + word_size)
{
*ctx->state_iter ^= to_le64(ctx->last_word);
++ctx->state_iter;
ctx->last_word = 0;
ctx->last_word_iter = (uint8_t*)&(ctx->last_word);
--state_unfilled_size;
}

// fill the state unfilled space with words until it's full
while(state_unfilled_size > 0 && size >= word_size)
{
*ctx->state_iter ^= load_le(data);
++ctx->state_iter;
data += word_size;
size -= word_size;
--state_unfilled_size;
}

// if the state is full, calculate keccak and reset the state iterator
if(ctx->state_iter == ctx->state + (ctx->block_size / word_size))
{
keccakf1600_best(ctx->state);
ctx->state_iter = ctx->state;
}

// if there is more data then block size, fill the whole blocks
if(size >= ctx->block_size)
{
while(size >= ctx->block_size)
{
for (i = 0; i < (ctx->block_size / word_size); ++i)
{
ctx->state[i] ^= load_le(data);
data += word_size;
}

keccakf1600_best(ctx->state);

size -= ctx->block_size;
}

ctx->state_iter = ctx->state;
}

// if there is more data then word size, fill the whole words
while (size >= word_size)
{
*ctx->state_iter ^= load_le(data);
++ctx->state_iter;
data += word_size;
size -= word_size;
}

// if there is still some data put it into last word.
while (size > 0)
{
*ctx->last_word_iter = *data;
++ctx->last_word_iter;
++data;
--size;
}

// if the last word is full, move it to state.
if(ctx->last_word_iter == (uint8_t*)&(ctx->last_word) + word_size)
{
*ctx->state_iter ^= to_le64(ctx->last_word);
++ctx->state_iter;
ctx->last_word = 0;
ctx->last_word_iter = (uint8_t*)&(ctx->last_word);
}

// if the state is full, calculate keccak and reset the state iterator
if(ctx->state_iter == ctx->state + (ctx->block_size / word_size))
{
keccakf1600_best(ctx->state);
ctx->state_iter = ctx->state;
}
}

static inline ALWAYS_INLINE void keccak_final(struct ethash_keccak256_context* ctx, uint64_t* out)
{
static const size_t word_size = sizeof(uint64_t);
size_t i;

*ctx->last_word_iter = 0x01;
*ctx->state_iter ^= to_le64(ctx->last_word);

ctx->state[(ctx->block_size / word_size) - 1] ^= 0x8000000000000000;

keccakf1600_best(ctx->state);

for (i = 0; i < (ctx->hash_size / word_size); ++i)
out[i] = to_le64(ctx->state[i]);
}

void ethash_keccak256_init(struct ethash_keccak256_context* ctx)
{
keccak_init(ctx, 256);
}

void ethash_keccak256_update(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size)
{
keccak_update(ctx, data, size);
}

union ethash_hash256 ethash_keccak256_final(struct ethash_keccak256_context* ctx)
{
union ethash_hash256 hash;
keccak_final(ctx, hash.word64s);
return hash;
}

static inline ALWAYS_INLINE void keccak_init_2(struct ethash_keccak256_context* ctx, size_t bits)
{
__builtin_memset((uint8_t*)ctx->state, 0, sizeof ctx->state);
ctx->state_iter = ctx->state;

ctx->hash_size = bits / 8;
ctx->block_size = (1600 - bits * 2) / 8;
ctx->last_word = 0;
ctx->last_word_iter = (uint8_t*)&ctx->last_word;

__builtin_memset(ctx->buffer, 0, sizeof ctx->buffer);
ctx->buffer_index = 0;
}

static inline ALWAYS_INLINE void keccak_update_2(
struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size)
{
static const size_t word_size = sizeof(uint64_t);

while(size > 0)
{
size_t empty_space_size = ctx->block_size - ctx->buffer_index;
size_t data_to_load_size = size >= empty_space_size ? empty_space_size : size;

__builtin_memcpy(&ctx->buffer[ctx->buffer_index], data, data_to_load_size);
ctx->buffer_index += data_to_load_size;
size -= data_to_load_size;
data += data_to_load_size;

if(ctx->buffer_index == ctx->block_size)
{
size_t i;
uint8_t* d = &ctx->buffer[0];

for (i = 0; i < (ctx->block_size / word_size); ++i)
{
*ctx->state_iter ^= load_le(d);
++ctx->state_iter;
d += word_size;
}

keccakf1600_best(ctx->state);
ctx->state_iter = ctx->state;
ctx->buffer_index = 0;
}
}
}

static inline ALWAYS_INLINE void keccak_final_2(struct ethash_keccak256_context* ctx, uint64_t* out)
{
static const size_t word_size = sizeof(uint64_t);
size_t i;

if(ctx->buffer_index != 0)
{
uint8_t* d = ctx->buffer;
for (i = 0; i < (ctx->buffer_index / word_size); ++i)
{
*ctx->state_iter ^= load_le(d);
++ctx->state_iter;
d += word_size;
}

size_t last_word_size = ctx->buffer_index % word_size;
d = &ctx->buffer[ctx->buffer_index - last_word_size];
ctx->last_word_iter = (uint8_t*)&ctx->last_word;

while (last_word_size > 0)
{
*ctx->last_word_iter = *d;
++ctx->last_word_iter;
++d;
--last_word_size;
}
}

*ctx->last_word_iter = 0x01;
*ctx->state_iter ^= to_le64(ctx->last_word);

ctx->state[(ctx->block_size / word_size) - 1] ^= 0x8000000000000000;

keccakf1600_best(ctx->state);

for (i = 0; i < (ctx->hash_size / word_size); ++i)
out[i] = to_le64(ctx->state[i]);
}

void ethash_keccak256_init_2(struct ethash_keccak256_context* ctx)
{
keccak_init_2(ctx, 256);
}

void ethash_keccak256_update_2(struct ethash_keccak256_context* ctx, const uint8_t* data, size_t size)
{
keccak_update_2(ctx, data, size);
}

union ethash_hash256 ethash_keccak256_final_2(struct ethash_keccak256_context* ctx)
{
union ethash_hash256 hash;
keccak_final_2(ctx, hash.word64s);
return hash;
}
Loading