-1

我在 C 中实现了 SHA-3 哈希算法,它适用于非空文件,但是当我尝试获取空文件的摘要时,我得到了错误的摘要。

例如,如果散列一个只有“a”字母的文件,我得到:

be5215abf72333a73b992dafdf4ab59884b948452e0015cfaddaa0b87a0e4515

但是,当我散列空文件时,我得到

000000000000000000000000000000000000000000000000000000000000000000

我期望:

a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a

https://en.wikipedia.org/wiki/SHA-3#Examples_of_SHA-3_variants

谁能告诉我我的实施有什么问题?

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#define index(x, y)    (((x) % 5) + 5 * ((y) % 5))
#define KECCAK_ROUNDS  24
#define Plen           200

typedef unsigned int uint;

const uint64_t round_constants[KECCAK_ROUNDS] =
{
    0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000,
    0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009,
    0x000000000000008A, 0x0000000000000088, 0x0000000080008009, 0x000000008000000A,
    0x000000008000808B, 0x800000000000008B, 0x8000000000008089, 0x8000000000008003,
    0x8000000000008002, 0x8000000000000080, 0x000000000000800A, 0x800000008000000A,
    0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008
};

/* --------------------------------------------------------------------------- */

static uint64_t
rol(uint64_t a, uint offset)
{
    if (offset == 0) {
        return a;
    }

    return (a << offset) | (a >> (64 - offset));
}

static void
theta(uint64_t *A)
{
    uint64_t C[5], D[5];

    for (uint x = 0; x < 5; ++x) {
        C[x] = 0;
        for (uint y = 0; y < 5; ++y) {
            C[x] ^= A[index(x, y)];
        }
    }
    for (uint x = 0; x < 5; ++x) {
        D[x] = rol(C[(x + 1) % 5], 1) ^ C[(x + 4) % 5];
    }
    for (uint x = 0; x < 5; ++x) {
        for (uint y = 0; y < 5; ++y) {
            A[index(x, y)] ^= D[x];
        }
    }
}

static void
rho(uint64_t *A)
{
    uint newX, newY, ro[KECCAK_ROUNDS];

    ro[index(0, 0)] = 0;
    uint x = 1;
    uint y = 0;
    for (uint t = 0; t < KECCAK_ROUNDS; ++t) {
        ro[index(x, y)] = ((t + 1) * (t + 2) / 2) % 64;
        newX = (0 * x + 1 * y) % 5;
        newY = (2 * x + 3 * y) % 5;
        x = newX;
        y = newY;
    }

    for (x = 0; x < 5; ++x) {
        for (y = 0; y < 5; ++y) {
            A[index(x, y)] = rol(A[index(x, y)], ro[index(x, y)]);
        }
    }
}

static void
pi(uint64_t *A)
{
    uint64_t tempA[25];

    for (uint x = 0; x < 5; ++x) {
        for (uint y = 0; y < 5; ++y) {
            tempA[index(x, y)] = A[index(x, y)];
        }
    }
    for (uint x = 0; x < 5; ++x) {
        for (uint y = 0; y < 5; ++y) {
            A[index(0 * x + 1 * y, 2 * x + 3 * y)] = tempA[index(x, y)];
        }
    }
}

static void
chi(uint64_t *A)
{
    uint64_t C[5];

    for (uint y = 0; y < 5; ++y) {
        for (uint x = 0; x < 5; ++x) {
            C[x] = A[index(x, y)] ^ ((~A[index(x + 1, y)]) & A[index(x + 2, y)]);
        }
        for (uint x = 0; x < 5; ++x) {
            A[index(x, y)] = C[x];
        }
    }
}

static void
iota(uint64_t *A, uint indexRound)
{
    A[index(0, 0)] ^= round_constants[indexRound];
}

static void
keccakf(void *state)
{
    for (uint i = 0; i < KECCAK_ROUNDS; ++i) {
        theta(state);
        rho(state);
        pi(state);
        chi(state);
        iota(state, i);
    }
}

static void
xorin(uint8_t *dest, const uint8_t *src, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        dest[i] ^= src[i];
    }
}
static void
setout(uint8_t *dest, const uint8_t *src, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        dest[i] = src[i];
    }
}

/* The sponge-based hash construction.  */
static void
hash(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen, size_t rate, uint8_t delim)
{
    if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
        return;
    }
    uint8_t a[Plen] = { 0 /* 0, 0, 0, ... */ };

    while (inlen >= rate) {
        xorin(a, in, rate);
        keccakf(a);
        in += rate;
        inlen -= rate;
    }

    a[inlen] ^= delim;
    a[rate - 1] ^= 0x80;

    xorin(a, in, inlen);

    keccakf(a);

    while (outlen >= rate) {
        setout(out, a, rate);
        keccakf(a);
        out += rate;
        outlen -= rate;
    }

    setout(out, a, outlen);
    memset(a, '\0', Plen);
}

int
sha3_stream (FILE *stream, void *resblock, size_t databitlen)
{
  size_t SHA3_BLOCK = databitlen / 8;
  size_t bytesread;
  uint8_t *in = malloc (databitlen);
  if (!in)
    return 1;

  while ((bytesread = fread(in, sizeof(char), SHA3_BLOCK, stream)) != 0)
    hash(resblock, SHA3_BLOCK, in, bytesread, Plen - (databitlen / 4), 0x06);

  free(in);

  return 0;
}

int main(int argc, char **argv)
{
  /* 32 it is SHA3-256 BLOCK */
  /* here used 256 bits digest lenght */
  for (int i = 1; i < argc; ++i)
    {
      uint8_t *out = malloc(32);
      FILE *fp = fopen(argv[i], "r");
      sha3_stream(fp, out, 256);

      for (int j = 0; j < 32; ++j)
        printf("%02x", out[j]);
      printf(" %s\n", argv[i]);

      free(out);
    }
}
4

1 回答 1

0

正如 user2722968 所说:“对于空文件,sha3_stream 中的 while 循环根本不会执行,因为您在第一次迭代时处于 EOF。因此 resblock 保持为零;切换到 EOF 条件为的 do-while 循环每次迭代后检查,而不是之前。”

所以,正确的代码是:

    bytesread = fread(in, sizeof(char), SHA3_BLOCK, stream);
    do {
        hash(resblock, SHA3_BLOCK, in, bytesread, Plen - (databitlen / 4), 0x06);
    } while ((bytesread = fread(in, sizeof(char), SHA3_BLOCK, stream)) != 0);

反而错了:

    while ((bytesread = fread(in, sizeof(char), SHA3_BLOCK, stream)) != 0)
      hash(resblock, SHA3_BLOCK, in, bytesread, Plen - (databitlen / 4), 0x06);
于 2019-05-17T11:21:02.297 回答