diff --git a/Makefile.am b/Makefile.am index f4b93625..39afb117 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,6 +124,8 @@ UTIL_FILES = \ hash/hmac.hpp \ hash/hotp.cpp \ hash/hotp.hpp \ + hash/keccak.cpp \ + hash/keccak.hpp \ hash/md2.cpp \ hash/md2.hpp \ hash/md4.cpp \ @@ -374,6 +376,7 @@ TEST_BIN = \ test/geom/ray \ test/hash/murmur \ test/hash/fasthash \ + test/hash/keccak \ test/hmac \ test/hotp \ test/hton \ diff --git a/hash/keccak.cpp b/hash/keccak.cpp new file mode 100644 index 00000000..b614c049 --- /dev/null +++ b/hash/keccak.cpp @@ -0,0 +1,485 @@ +#include "./keccak.hpp" + +#include "../endian.hpp" +#include "../maths.hpp" +#include "../bitwise.hpp" + +#include +#include +#include +#include + +#if 0 +#define FOR(i,n) for(i=0; i>1; } +#define ROL(a,o) ((((u64)a)<>(64-o))) +static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; } +static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } } +static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } } +#define rL(x,y) load64((u8*)s+8*(x+5*y)) +#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l) +#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l) +void KeccakF1600(void *s) +{ + ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D; + for(i=0; i<24; i++) { + /*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); } + /*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; } + /*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); } + /*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<0) { b=(inLen0) { b=(outLen0) KeccakF1600(s); } +} + +#else + +// derived from Keccak (KCP) readable-and-compact C implementation + +/* +Implementation by the Keccak, Keyak and Ketje Teams, namely, Guido Bertoni, +Joan Daemen, Michaël Peeters, Gilles Van Assche and Ronny Van Keer, hereby +denoted as "the implementer". + +For more information, feedback or questions, please refer to our websites: +http://keccak.noekeon.org/ +http://keyak.noekeon.org/ +http://ketje.noekeon.org/ + +To the extent possible under law, the implementer has waived all copyright +and related or neighboring rights to the source code in this file. +http://creativecommons.org/publicdomain/zero/1.0/ +*/ + +/* +================================================================ +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. +================================================================ +*/ + +/** + * Function to compute the Keccak[r, c] sponge function over a given input. + * @param rate The value of the rate r. + * @param capacity The value of the capacity c. + * @param input Pointer to the input message. + * @param inputByteLen The number of input bytes provided in the input message. + * @param delimitedSuffix Bits that will be automatically appended to the end + * of the input message, as in domain separation. + * This is a byte containing from 0 to 7 bits + * These n bits must be in the least significant bit positions + * and must be delimited with a bit 1 at position n + * (counting from 0=LSB to 7=MSB) and followed by bits 0 + * from position n+1 to position 7. + * Some examples: + * - If no bits are to be appended, then @a delimitedSuffix must be 0x01. + * - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), @a delimitedSuffix must be 0x06. + * - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), @a delimitedSuffix must be 0x1F. + * - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, @a delimitedSuffix must be 0x8B. + * @param output Pointer to the buffer where to store the output. + * @param outputByteLen The number of output bytes desired. + * @pre One must have r+c=1600 and the rate a multiple of 8 bits in this implementation. + */ + +/** + * Function to compute SHAKE128 on the input message with any output length. + */ +void +FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen) +{ + keccak k (1344, 256, 0x1f); + k.update (input, inputByteLen); + k.digest (output, outputByteLen); +} + +/** + * Function to compute SHAKE256 on the input message with any output length. + */ +void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen) +{ + keccak k (1088, 512, 0x1f); + k.update (input, inputByteLen); + k.digest (output, outputByteLen); +} + +/** + * Function to compute SHA3-224 on the input message. The output length is fixed to 28 bytes. + */ +void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (1152, 448, 0x06); + + for (unsigned int i = 0; i < inputByteLen; ++i) + k.update (&input[i], 1); + //k.update (input, inputByteLen); + k.digest (output, 28); +} + +/** + * Function to compute SHA3-256 on the input message. The output length is fixed to 32 bytes. + */ +void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (1088, 512, 0x06); + k.update (input, inputByteLen); + k.digest (output, 32); +} + +/** + * Function to compute SHA3-384 on the input message. The output length is fixed to 48 bytes. + */ +void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (832, 768, 0x06); + k.update (input, inputByteLen); + k.digest (output, 48); +} + +/** + * Function to compute SHA3-512 on the input message. The output length is fixed to 64 bytes. + */ +void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output) +{ + keccak k (576, 1024, 0x06); + k.update (input, inputByteLen); + k.digest (output, 64); +} + + +/* +================================================================ +A readable and compact implementation of the Keccak-f[1600] permutation. +================================================================ +*/ + +static constexpr +size_t +i (size_t x, size_t y) +{ + return x + 5 * y; +} + + +/** + * Function that computes the linear feedback shift register (LFSR) used to + * define the round constants (see [Keccak Reference, Section 1.2]). + */ +class lfsr86540 { +public: + lfsr86540 (): + value (0x1) + { ; } + + + bool + update (void) + { + bool result = value & 0x01; + if (value & 0x80) + // Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1 + value = (value << 1) ^ 0x71; + else + value <<= 1; + return result; + } + + +private: + uint8_t value; +}; + + +// θ step, see [Keccak Reference, Section 2.3.2] +static void +permute_theta (uint64_t m_words[5][5]) +{ + uint64_t C[5], D; + + // Compute the parity of the columns + for (unsigned x = 0; x < 5; ++x) + C[x] = m_words[0][x] ^ m_words[1][x] ^ m_words[2][x] ^ m_words[3][x] ^ m_words[4][x]; + + for (unsigned x = 0; x < 5; ++x) { + // Compute the θ effect for a given column + D = C[(x+4)%5] ^ rotatel (C[(x+1)%5], 1); + + // Add the θ effect to the whole column + for (unsigned y = 0; y < 5; ++y) + m_words[y][x] ^= D; + } +} + + +void +permute_rho (uint64_t m_words[5][5]) +{ + m_words[0][1] = rotatel (m_words[0][1], 1); + m_words[0][2] = rotatel (m_words[0][2], 62); + m_words[0][3] = rotatel (m_words[0][3], 28); + m_words[0][4] = rotatel (m_words[0][4], 27); + m_words[1][0] = rotatel (m_words[1][0], 36); + m_words[1][1] = rotatel (m_words[1][1], 44); + m_words[1][2] = rotatel (m_words[1][2], 6); + m_words[1][3] = rotatel (m_words[1][3], 55); + m_words[1][4] = rotatel (m_words[1][4], 20); + m_words[2][0] = rotatel (m_words[2][0], 3); + m_words[2][1] = rotatel (m_words[2][1], 10); + m_words[2][2] = rotatel (m_words[2][2], 43); + m_words[2][3] = rotatel (m_words[2][3], 25); + m_words[2][4] = rotatel (m_words[2][4], 39); + m_words[3][0] = rotatel (m_words[3][0], 41); + m_words[3][1] = rotatel (m_words[3][1], 45); + m_words[3][2] = rotatel (m_words[3][2], 15); + m_words[3][3] = rotatel (m_words[3][3], 21); + m_words[3][4] = rotatel (m_words[3][4], 8); + m_words[4][0] = rotatel (m_words[4][0], 18); + m_words[4][1] = rotatel (m_words[4][1], 2); + m_words[4][2] = rotatel (m_words[4][2], 61); + m_words[4][3] = rotatel (m_words[4][3], 56); + m_words[4][4] = rotatel (m_words[4][4], 14); + return; + + + for (size_t i = 1; i < 25; ++i) { + //unsigned r = ((t+1)*(t+2)/2)%64; + unsigned r = ((i + 1) * (i + 2) / 2) % 64; + + m_words[i/5][i%5] = rotatel (m_words[i/5][i%5], r); + } +} + + +void +permute_pi (uint64_t m_words[5][5]) +{ + //auto A = reinterpret_cast (m_words); + + //uint64_t A1; + //A1 = A[1]; + //A[ 1] = A[ 6]; + //A[ 6] = A[ 9]; + //A[ 9] = A[22]; + //A[22] = A[14]; + //A[14] = A[20]; + //A[20] = A[ 2]; + //A[ 2] = A[12]; + //A[12] = A[13]; + //A[13] = A[19]; + //A[19] = A[23]; + //A[23] = A[15]; + //A[15] = A[ 4]; + //A[ 4] = A[24]; + //A[24] = A[21]; + //A[21] = A[ 8]; + //A[ 8] = A[16]; + //A[16] = A[ 5]; + //A[ 5] = A[ 3]; + //A[ 3] = A[18]; + //A[18] = A[17]; + //A[17] = A[11]; + //A[11] = A[ 7]; + //A[ 7] = A[10]; + //A[10] = A1; + //return; + + unsigned x = 1, y = 0; + uint64_t current = m_words[y][x]; + uint64_t temp; + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (unsigned t = 0; t < 24; ++t) { + unsigned int Y = (2*x+3*y)%5; + x = y; + y = Y; + + temp = m_words[y][x]; + m_words[y][x] = current; + current = temp; + } + + //for (unsigned int i = 0; i < 5; ++i) + // for (unsigned int j = 0; j < 5; ++j) + // m_words[j][(2*i+3*j)%5] = m_words[i][j]; +} + + +/** + * Function that computes the Keccak-f[1600] permutation on the given state. + */ +void +keccak::permute (void) +{ + for (size_t i = 0; i < m_bitrate/64; ++i) + m_words[i/5][i%5] = ltoh (m_words[i/5][i%5]); + + lfsr86540 shift; + + for (unsigned round = 0; round < 24; ++round) { + permute_theta (m_words); + permute_rho (m_words); + permute_pi (m_words); + + + if (0) { // === ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) === + uint64_t current, temp; + // Start at coordinates (1 0) + unsigned x = 1, y = 0; + current = m_words[y][x]; + // Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23 + for (unsigned 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 = m_words[y][x]; + m_words[y][x] = rotatel (current, r); + current = temp; + } + } + + { // === χ step (see [Keccak Reference, Section 2.3.1]) === + uint64_t temp[5]; + for (unsigned y = 0; y < 5; ++y) { + // Take a copy of the plane + for (unsigned x = 0; x < 5; ++x) + temp[x] = m_words[y][x]; + + // Compute χ on the plane + for(unsigned x = 0; x < 5; ++x) + m_words[y][x] = temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]); + } + } + + { // === ι step (see [Keccak Reference, Section 2.3.5]) === + for (unsigned j = 0; j < 7; ++j) { + unsigned int bitPosition = (1 << j) - 1; //2^j-1 + if (shift.update ()) + m_words[0][0] ^= uint64_t{1} << bitPosition; + } + } + } +} + +/* +================================================================ +A readable and compact implementation of the Keccak sponge functions +that use the Keccak-f[1600] permutation. +================================================================ +*/ + +#include + + +void +keccak::update ( + const uint8_t *input, + size_t len +) { + unsigned int byterate = m_bitrate / 8; + + while (len) { + auto chunk = util::min (len, byterate - m_cursor); + + for (unsigned i = 0; i < chunk; ++i) + m_bytes[m_cursor++] ^= *input++; + + len -= chunk; + + if (m_cursor == byterate) { + permute (); + m_cursor = 0; + } + } +} + + +void +keccak::digest ( + uint8_t *output, + size_t len +) { + unsigned byterate = m_bitrate / 8u; + + // === 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) + m_bytes[m_cursor] ^= m_suffix; + + // If the first bit of padding is at position rate-1, we need a whole new + // block for the second bit of padding + if (m_suffix & 0x80 && m_cursor == byterate - 1) + permute (); + + // Add the second bit of padding + m_bytes[byterate - 1] ^= 0x80; + + // === Squeeze out all the output blocks === + while (len) { + permute (); + + auto chunk = util::min (len, byterate); + std::copy_n (m_bytes.begin (), chunk, output); + + output += chunk; + len -= chunk; + } +} + + +keccak::keccak (unsigned _bitrate, + unsigned _capacity, + uint8_t _suffix): + m_bitrate (_bitrate), + m_capacity (_capacity), + m_suffix (_suffix), + m_cursor (0) +{ + // we could support bitrates that are multiples of 8, but 64 simplifies + // some state handling, and the SHA-3 constants are all multiples of 64 + // bits anyway. + if ((m_bitrate + m_capacity) / 8 != sizeof (m_bytes) || m_bitrate % 64 != 0) + throw "error"; + + std::fill (std::begin (m_bytes), std::end (m_bytes), 0); +} + + + +#endif diff --git a/hash/keccak.hpp b/hash/keccak.hpp new file mode 100644 index 00000000..682146d9 --- /dev/null +++ b/hash/keccak.hpp @@ -0,0 +1,53 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2016 Danny Robson + */ + +#ifndef __HASH_KECCAK_HPP +#define __HASH_KECCAK_HPP + +#include +#include + +class keccak { +public: + keccak (unsigned bitrate, unsigned capacity, uint8_t suffix); + + void update (const uint8_t *input, size_t len); + void digest (uint8_t *output, size_t len); + +private: + void permute (void); + + const unsigned m_bitrate; + const unsigned m_capacity; + const uint8_t m_suffix; + + size_t m_cursor; + + union { + std::array m_bytes; + uint64_t m_words[5][5]; + }; +}; + + +void FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen); +void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen); +void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output); +void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output); + +#endif diff --git a/test/hash/keccak.cpp b/test/hash/keccak.cpp new file mode 100644 index 00000000..45fc7487 --- /dev/null +++ b/test/hash/keccak.cpp @@ -0,0 +1,179 @@ +#include "hash/keccak.hpp" +#include "debug.hpp" +#include "tap.hpp" + +#include + + +static constexpr +uint8_t +from_hex (char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return 10 + c - 'A'; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return 10 + c - 'a'; + } + + unreachable (); +} + + +template +static constexpr +std::array +make_array (const char (&str)[S]) +{ + if (S < 1) + return {}; + + static_assert (S % 2 == 1, "requires full bytes + null"); + std::array out {}; + + for (size_t i = 0; i < S - 1; i+=2) { + out[i / 2] = +from_hex (str[i]) << 4 | +from_hex (str[i+1]); + } + + return out; +} + + +template +static +std::vector +make_vector (const char (&str)[S]) { + const auto arr = make_array (str); + return std::vector (arr.cbegin (), arr.cend ()); +} + + +int +main (int, char**) +{ + util::TAP::logger tap; + + static const struct { + const char *msg; + std::vector data; + + std::array sha3_224; + std::array sha3_256; + std::array sha3_384; + std::array sha3_512; + } TESTS[] = { + { + " empty string", + { }, + make_array ("6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7"), + make_array ("A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A"), + make_array ("0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004"), + make_array ("A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26"), + }, + { + " 8-bit string", + make_vector ("CC"), + make_array ("DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"), + make_array ("677035391CD3701293D385F037BA32796252BB7CE180B00B582DD9B20AAAD7F0"), + make_array ("5EE7F374973CD4BB3DC41E3081346798497FF6E36CB9352281DFE07D07FC530CA9AD8EF7AAD56EF5D41BE83D5E543807"), + make_array ("3939FCC8B57B63612542DA31A834E5DCC36E2EE0F652AC72E02624FA2E5ADEECC7DD6BB3580224B4D6138706FC6E80597B528051230B00621CC2B22999EAA205"), + }, + { + " 16-bit string", + make_vector ("41FB"), + make_array ("BFF295861DAEDF33E70519B1E2BCB4C2E9FE3364D789BC3B17301C15"), + make_array ("39F31B6E653DFCD9CAED2602FD87F61B6254F581312FB6EEEC4D7148FA2E72AA"), + make_array ("1DD81609DCC290EFFD7AC0A95D4A20821580E56BD50DBD843920650BE7A80A1719577DA337CFDF86E51C764CAA2E10BD"), + make_array ("AA092865A40694D91754DBC767B5202C546E226877147A95CB8B4C8F8709FE8CD6905256B089DA37896EA5CA19D2CD9AB94C7192FC39F7CD4D598975A3013C69"), + }, + { + "224-bit string", + make_vector ("0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E"), + make_array ("1E693B0BCE2372550DAEF35B14F13AB43441ED6742DEE3E86FD1D8EF"), + make_array ("6DE164A9626D5A4F54D854AC158994F35A8E362ECC753F55182790934A2E0D06"), + make_array ("641A7AF13B889D1A0F1AA3E4E4FF8CC5903C47E1A52BDEA257D80E37E596564AB33EEAD06717CDB6B706CB6986293D4F"), + make_array ("21132FC11F6040AD493D627027C752CE29816589DE7BE78562914B63D1A9219803DDBD9673AA749F37FF4D6E1B5AE2A12633BA8B0C9994E031EBF6C42E58A793"), + }, + { + "256-bit string", + make_vector ("9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10"), + make_array ("887921848AD98458F3DB3E0ECD5AD5DB1F0BF9F2D0CA08601074D597"), + make_array ("2F1A5F7159E34EA19CDDC70EBF9B81F1A66DB40615D7EAD3CC1F1B954D82A3AF"), + make_array ("BAAE7AAED4FBF42F9316C7E8F722EEB06A598B509F184B22FBD5A81C93D95FFF711F5DE90847B3248B6DF76CABCE07EE"), + make_array ("B087C90421AEBF87911647DE9D465CBDA166B672EC47CCD4054A7135A1EF885E7903B52C3F2C3FE722B1C169297A91B82428956A02C631A2240F12162C7BC726"), + }, + { + "384-bit string", + make_vector ("D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9"), + make_array ("B7A51FBB084DEEB55136EFD7260E5B112E3C40D1A2D14B142DF930DF"), + make_array ("34F8607EC10C092C1BA0B6565CE6197062C4E1A35A8E8C723E48A2D2416C3790"), + make_array ("A127FEFCDD240F762CCE3F5F1551FC7E1CDEBC7950D1CD94C6888F490CB2285A10FD0EE797B168C5CA4761FA232AAF05"), + make_array ("7EF3A2894C6ECBC4201B15348F90671515ACCBA3C8166621F864A9184BF08C3F5A895F6B599D3CB41F20A8A1DF25AE84F1A6D7C8DE74FB7CEF48F7E96FDE8D43"), + }, + { + "512-bit string", + make_vector ("E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7"), + make_array ("C154607F986F9BF902D831293C8386D36B201EABA6F6FB0B678B4B81"), + make_array ("27A6441EE939B46E2C378D7AFEB0E891C47A28120E488EFF0AB71AF08788CEB3"), + make_array ("423BA134D3BCB5E440AC83372C7EDDBA3AE3BDDF1222F505C19CDE246AD76A2B0D07239A54E1D0934C9B3D29D49E5FBD"), + make_array ("EB5067BF762A291CF258AD69A816A0B089E0BD44F8E5B74CF60BCE64734E59853CCB8D091CD2E33F90AA063FB7942CF5965D459200144C1A0801ABD69A9A094A"), + }, + { + "520-bit string", + make_vector ("16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00"), + make_array ("95E87AC90F541AB90CBCF7FD7E0E0C152CEF78D5EE1830E9ED8A1ED7"), + make_array ("C4BB067383002DB44CA773918BB74104B604A583E12B06BE56C270F8B43512F2"), + make_array ("662C4851D311A786DE4CDA7E9EA1EFF0BFA462761FF6CF804E591ED9A15B0DC93A2BB6A6CFFDC8D7D23A233A52C86EAD"), + make_array ("B0E23D600BA4215F79D50047BBFED50DF7D6E769514D796AFD166DEECA88BD1CBE0AFC72A41E0317A223225B4F5882F723AFCBA3AF7C457EB525946DA6C53BB0"), + }, + { + "1,000,000 'a' string", + std::vector (1'000'000, 'a'), + make_array ("d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"), + make_array ("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"), + make_array ("eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340"), + make_array ("3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"), + } + }; + + for (const auto &t: TESTS) { + #define TEST(WIDTH) \ + do { \ + std::array sha3_##WIDTH; \ + std::fill (std::begin (sha3_##WIDTH), std::end (sha3_##WIDTH), 0); \ + \ + FIPS202_SHA3_##WIDTH (t.data.data (), t.data.size (), &sha3_##WIDTH[0]); \ + \ + tap.expect_eq (t.sha3_##WIDTH, sha3_##WIDTH, "sha3-%u %s", unsigned (WIDTH), t.msg); \ + } while (0) + + TEST(224); + TEST(256); + TEST(384); + TEST(512); + } + + return tap.status (); +}