[monero-project/monero] Incremental Keccak API (#4259)

moneromooo-monero commented on this pull request.

> + // clear the rest of the data queue
+ memset((char*)ctx->message + ctx->rest, 0, KECCAK_BLOCKLEN — ctx->rest);
+ ((char*)ctx->message)[ctx->rest] |= 0x01;
+ ((char*)ctx->message)[KECCAK_BLOCKLEN — 1] |= 0x80;
+
+ // process final block
+ KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+ ctx->rest = KECCAK_FINALIZED; // mark context as finalized
+ }
+
+ static_assert(KECCAK_BLOCKLEN > KECCAK_DIGESTSIZE, «»);
+ if (md) {
+ memcpy(md, ctx->hash, KECCAK_DIGESTSIZE);
+ }
+
+ memset(ctx, 0, sizeof(KECCAK_CTX));

Could possibly do with a memwipe. Not sure if really necessary.

> +
+ // fill partial block
+ if (idx) {
+ size_t left = KECCAK_BLOCKLEN — idx;
+ memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left)); + if (inlen < left) return; + + // process partial block + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+
+ in += left;
+ inlen -= left;
+ }
+ while (inlen >= KECCAK_BLOCKLEN) {
+ uint64_t* aligned_message_block;
+ if (IS_ALIGNED_64(in)) {

KECCAK_BLOCKLEN is a multiple of 8, so this calc is only needed once. In fact, if not aligned, a prefix can be memcpy’d, then an aligned loop can follow. Might end up more complicated, not sure. It’d also allow the round function to xor a uint64_t at a time in that case.

> +#define KECCAK_BLOCKLEN 136
+#define KECCAK_DIGESTSIZE 32
+#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) — (const char*)0)))
+#define KECCAK_PROCESS_BLOCK(st, block) { \
+ for (int i_ = 0; i_ < KECCAK_BLOCKLEN; i_++){ \ + ((char*)(st))[i_] ^= ((char*) (block))[i_]; \ + }; \ + keccakf(st, KECCAK_ROUNDS); } + + +void keccak_init(KECCAK_CTX * ctx){ + memset(ctx, 0, sizeof(KECCAK_CTX)); +} + +void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){ + size_t idx = (size_t)ctx->rest;

Maybe make rest size_t, it’d avoid needless word size conversions.

> @@ -15,6 +15,17 @@
#define ROTL64(x, y) (((x) << (y)) | ((x) >> (64 — (y))))
#endif

+// SHA3 Algorithm context.
+typedef struct KECCAK_CTX
+{
+ // 1600 bits algorithm hashing state
+ uint64_t hash[25];
+ // 1536-bit buffer for leftovers
+ uint64_t message[24];

That seems too large. Looks like it needs 136 bytes, so 17 uint64_t. Is this wrong ?

> +#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) — (const char*)0)))
+#define KECCAK_PROCESS_BLOCK(st, block) { \
+ for (int i_ = 0; i_ < KECCAK_BLOCKLEN; i_++){ \ + ((char*)(st))[i_] ^= ((char*) (block))[i_]; \ + }; \ + keccakf(st, KECCAK_ROUNDS); } + + +void keccak_init(KECCAK_CTX * ctx){ + memset(ctx, 0, sizeof(KECCAK_CTX)); +} + +void keccak_update(KECCAK_CTX * ctx, const uint8_t *in, size_t inlen){ + size_t idx = (size_t)ctx->rest;
+
+ if (ctx->rest & KECCAK_FINALIZED) return; // too late for additional input

That might be worth a hard error, it’s a programming error.

> + size_t left = KECCAK_BLOCKLEN — idx;
+ memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left)); + if (inlen < left) return; + + // process partial block + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+
+ in += left;
+ inlen -= left;
+ }
+ while (inlen >= KECCAK_BLOCKLEN) {
+ uint64_t* aligned_message_block;
+ if (IS_ALIGNED_64(in)) {
+ // the most common case is processing of an already aligned message
+ // without copying it
+ aligned_message_block = (uint64_t*)(void*)in;

The double cast seems unneeded.

> + ctx->rest = (unsigned)((ctx->rest + inlen) % KECCAK_BLOCKLEN);
+
+ // fill partial block
+ if (idx) {
+ size_t left = KECCAK_BLOCKLEN — idx;
+ memcpy((char*)ctx->message + idx, in, (inlen < left ? inlen : left)); + if (inlen < left) return; + + // process partial block + KECCAK_PROCESS_BLOCK(ctx->hash, ctx->message);
+
+ in += left;
+ inlen -= left;
+ }
+ while (inlen >= KECCAK_BLOCKLEN) {
+ uint64_t* aligned_message_block;

Could be const, it’s input.

Добавить комментарий