Skip to content

File libkeccak.h

File List > code_source > templat > include > TempLat > util > hash > libkeccak.h

Go to the documentation of this file

#ifndef INSIDE_TempLat_UTIL_HASH_LIBKECCAK_TWOENDIANS_H
#error Do not include this file anywhere else than in the controlled header keccakhash.h
#endif

/* this header will be included twice, so we create two header guards: */
#if !defined(TempLat_UTIL_HASH_LIBKECCAK_H_LITTLE_ENDIAN) || !defined(TempLat_UTIL_HASH_LIBKECCAK_H_BIG_ENDIAN)

#ifdef LITTLE_ENDIAN
#define TEMPLAT_UTIL_HASH_LIBKECCAK_H_LITTLE_ENDIAN
#else
#define TEMPLAT_UTIL_HASH_LIBKECCAK_H_BIG_ENDIAN
#endif

// #include <cstdint>
// #include <cstddef>
//
// #include <string.h>

/*
 ================================================================
 The purpose of this source file is to demonstrate a readable and compact
 implementation of all the Keccak instances approved in the FIPS 202 standard,
 including the hash functions and the extendable-output functions (XOFs).

 We focused on clarity and on source-code compactness,
 rather than on the performance.

 The advantages of this implementation are:
 + The source code is compact, after removing the comments, that is. :-)
 + There are no tables with arbitrary constants.
 + For clarity, the comments link the operations to the specifications using
 the same notation as much as possible.
 + There is no restriction in cryptographic features. In particular,
 the SHAKE128 and SHAKE256 XOFs can produce any output length.
 + The code does not use much RAM, as all operations are done in place.

 The drawbacks of this implementation are:
 - There is no message queue. The whole message must be ready in a buffer.
 - It is not optimized for peformance.

 The implementation is even simpler on a little endian platform. Just define the
 LITTLE_ENDIAN symbol in that case.

 For a more complete set of implementations, please refer to
 the Keccak Code Package at https://github.com/gvanas/KeccakCodePackage

 For more information, please refer to:
 * [Keccak Reference] http://keccak.noekeon.org/Keccak-reference-3.0.pdf
 * [Keccak Specifications Summary] http://keccak.noekeon.org/specs_summary.html

 This file uses UTF-8 encoding, as some comments use Greek letters.
 ================================================================
 */

// void Keccak(unsigned int rate, unsigned int capacity, const uint8_t *input, unsigned long long int inputByteLen,
// uint8_t delimitedSuffix, uint8_t *output, unsigned long long int outputByteLen);

struct TrueKeccak {

  void FIPS202_SHAKE128(const uint8_t *input, unsigned int inputByteLen, uint8_t *output, int outputByteLen)
  {
    Keccak(1344, 256, input, inputByteLen, 0x1F, output, outputByteLen);
  }

  void FIPS202_SHAKE256(const uint8_t *input, unsigned int inputByteLen, uint8_t *output, int outputByteLen)
  {
    Keccak(1088, 512, input, inputByteLen, 0x1F, output, outputByteLen);
  }

  void FIPS202_SHA3_224(const uint8_t *input, unsigned int inputByteLen, uint8_t *output)
  {
    Keccak(1152, 448, input, inputByteLen, 0x06, output, 28);
  }

  void FIPS202_SHA3_256(const uint8_t *input, unsigned int inputByteLen, uint8_t *output)
  {
    Keccak(1088, 512, input, inputByteLen, 0x06, output, 32);
  }

  void FIPS202_SHA3_384(const uint8_t *input, unsigned int inputByteLen, uint8_t *output)
  {
    Keccak(832, 768, input, inputByteLen, 0x06, output, 48);
  }

  void FIPS202_SHA3_512(const uint8_t *input, unsigned int inputByteLen, uint8_t *output)
  {
    Keccak(576, 1024, input, inputByteLen, 0x06, output, 64);
  }

  /*
   ================================================================
   Technicalities
   ================================================================
   */

  //  typedef uint8_t UINT8;
  //  typedef unsigned long long int UINT64;
  //  typedef UINT64 tKeccakLane;
  // WV:
  typedef uint8_t UINT8;
  typedef uint64_t UINT64;
  typedef UINT64 tKeccakLane;

#ifndef LITTLE_ENDIAN
  static UINT64 load64(const UINT8 *x)
  {
    int i;
    UINT64 u = 0;

    for (i = 7; i >= 0; --i) {
      u <<= 8;
      u |= x[i];
    }
    return u;
  }

  static void store64(UINT8 *x, UINT64 u)
  {
    unsigned int i;

    for (i = 0; i < 8; ++i) {
      x[i] = u;
      u >>= 8;
    }
  }

  static void xor64(UINT8 *x, UINT64 u)
  {
    unsigned int i;

    for (i = 0; i < 8; ++i) {
      x[i] ^= u;
      u >>= 8;
    }
  }
#endif

  /*
   ================================================================
   A readable and compact implementation of the Keccak-f[1600] permutation.
   ================================================================
   */

// WV Modification: since we now include this header twice, need to undefine at the end of the file.
#define ROL64(a, offset) ((((UINT64)a) << offset) ^ (((UINT64)a) >> (64 - offset)))
#define i(x, y) ((x) + 5 * (y))

#ifdef LITTLE_ENDIAN
#define readLane(x, y) (((tKeccakLane *)state)[i(x, y)])
#define writeLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) = (lane)
#define XORLane(x, y, lane) (((tKeccakLane *)state)[i(x, y)]) ^= (lane)
#else
#define readLane(x, y) load64((UINT8 *)state + sizeof(tKeccakLane) * i(x, y))
#define writeLane(x, y, lane) store64((UINT8 *)state + sizeof(tKeccakLane) * i(x, y), lane)
#define XORLane(x, y, lane) xor64((UINT8 *)state + sizeof(tKeccakLane) * i(x, y), lane)
#endif

  int LFSR86540(UINT8 *LFSR)
  {
    int result = ((*LFSR) & 0x01) != 0;
    if (((*LFSR) & 0x80) != 0) /* Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 */
      (*LFSR) = ((*LFSR) << 1) ^ 0x71;
    else
      (*LFSR) <<= 1;
    return result;
  }

  void KeccakF1600_StatePermute(void *state)
  {
    unsigned int round, x, y, j, t;
    UINT8 LFSRstate = 0x01;

    for (round = 0; round < 24; round++) {
      { /* === θ step (see [Keccak Reference, Section 2.3.2]) === */
        tKeccakLane C[5], D;

        /* Compute the parity of the columns */
        for (x = 0; x < 5; x++)
          C[x] = readLane(x, 0) ^ readLane(x, 1) ^ readLane(x, 2) ^ readLane(x, 3) ^ readLane(x, 4);
        for (x = 0; x < 5; x++) {
          /* Compute the θ effect for a given column */
          D = C[(x + 4) % 5] ^ ROL64(C[(x + 1) % 5], 1);
          /* Add the θ effect to the whole column */
          for (y = 0; y < 5; y++)
            XORLane(x, y, D);
        }
      }

      { /* === ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) === */
        tKeccakLane current, temp;
        /* Start at coordinates (1 0) */
        x = 1;
        y = 0;
        current = readLane(x, y);
        /* Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 */
        for (t = 0; t < 24; t++) {
          /* Compute the rotation constant r = (t+1)(t+2)/2 */
          unsigned int r = ((t + 1) * (t + 2) / 2) % 64;
          /* Compute ((0 1)(2 3)) * (x y) */
          unsigned int Y = (2 * x + 3 * y) % 5;
          x = y;
          y = Y;
          /* Swap current and state(x,y), and rotate */
          temp = readLane(x, y);
          writeLane(x, y, ROL64(current, r));
          current = temp;
        }
      }

      { /* === χ step (see [Keccak Reference, Section 2.3.1]) === */
        tKeccakLane temp[5];
        for (y = 0; y < 5; y++) {
          /* Take a copy of the plane */
          for (x = 0; x < 5; x++)
            temp[x] = readLane(x, y);
          /* Compute χ on the plane */
          for (x = 0; x < 5; x++)
            writeLane(x, y, temp[x] ^ ((~temp[(x + 1) % 5]) & temp[(x + 2) % 5]));
        }
      }

      { /* === ι step (see [Keccak Reference, Section 2.3.5]) === */
        for (j = 0; j < 7; j++) {
          unsigned int bitPosition = (1 << j) - 1; /* 2^j-1 */
          if (LFSR86540(&LFSRstate)) XORLane(0, 0, (tKeccakLane)1 << bitPosition);
        }
      }
    }
  }

  /*
   ================================================================
   A readable and compact implementation of the Keccak sponge functions
   that use the Keccak-f[1600] permutation.
   ================================================================
   */

  //  #include <string.h>
  //  #include <string>
  //  #define MIN(a, b) ((a) < (b) ? (a) : (b))

  void Keccak(unsigned int rate, unsigned int capacity, const uint8_t *input, unsigned long long int inputByteLen,
              uint8_t delimitedSuffix, uint8_t *output, unsigned long long int outputByteLen)
  {
    // WV: MIN
    auto MIN = [](auto &&a, auto &&b) { return (a) < (b) ? (a) : (b); };

    UINT8 state[200];
    unsigned int rateInBytes = rate / 8;
    unsigned int blockSize = 0;
    unsigned int i;

    if (((rate + capacity) != 1600) || ((rate % 8) != 0)) return;

    /* === Initialize the state === */
    memset(state, 0, sizeof(state));

    /* === Absorb all the input blocks === */
    while (inputByteLen > 0) {
      blockSize = MIN(inputByteLen, rateInBytes);
      for (i = 0; i < blockSize; i++)
        state[i] ^= input[i];
      input += blockSize;
      inputByteLen -= blockSize;

      if (blockSize == rateInBytes) {
        KeccakF1600_StatePermute(state);
        blockSize = 0;
      }
    }

    /* === Do the padding and switch to the squeezing phase === */
    /* Absorb the last few bits and add the first bit of padding (which coincides with the delimiter in delimitedSuffix)
     */
    state[blockSize] ^= delimitedSuffix;
    /* If the first bit of padding is at position rate-1, we need a whole new block for the second bit of padding */
    if (((delimitedSuffix & 0x80) != 0) && (blockSize == (rateInBytes - 1))) KeccakF1600_StatePermute(state);
    /* Add the second bit of padding */
    state[rateInBytes - 1] ^= 0x80;
    /* Switch to the squeezing phase */
    KeccakF1600_StatePermute(state);

    /* === Squeeze out all the output blocks === */
    while (outputByteLen > 0) {
      blockSize = MIN(outputByteLen, rateInBytes);
      memcpy(output, state, blockSize);
      output += blockSize;
      outputByteLen -= blockSize;

      if (outputByteLen > 0) KeccakF1600_StatePermute(state);
    }
  }
};
// WV Modification: since we now include this header twice, need to undefine at the end of the file.
#undef ROL64
#undef i
#undef readLane
#undef writeLane
#undef XORLane

#endif