hash/keccak: add initial sha3 base
This commit is contained in:
parent
a0d02c2fdb
commit
8d191fb2e1
@ -124,6 +124,8 @@ UTIL_FILES = \
|
|||||||
hash/hmac.hpp \
|
hash/hmac.hpp \
|
||||||
hash/hotp.cpp \
|
hash/hotp.cpp \
|
||||||
hash/hotp.hpp \
|
hash/hotp.hpp \
|
||||||
|
hash/keccak.cpp \
|
||||||
|
hash/keccak.hpp \
|
||||||
hash/md2.cpp \
|
hash/md2.cpp \
|
||||||
hash/md2.hpp \
|
hash/md2.hpp \
|
||||||
hash/md4.cpp \
|
hash/md4.cpp \
|
||||||
@ -374,6 +376,7 @@ TEST_BIN = \
|
|||||||
test/geom/ray \
|
test/geom/ray \
|
||||||
test/hash/murmur \
|
test/hash/murmur \
|
||||||
test/hash/fasthash \
|
test/hash/fasthash \
|
||||||
|
test/hash/keccak \
|
||||||
test/hmac \
|
test/hmac \
|
||||||
test/hotp \
|
test/hotp \
|
||||||
test/hton \
|
test/hton \
|
||||||
|
485
hash/keccak.cpp
Normal file
485
hash/keccak.cpp
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
#include "./keccak.hpp"
|
||||||
|
|
||||||
|
#include "../endian.hpp"
|
||||||
|
#include "../maths.hpp"
|
||||||
|
#include "../bitwise.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define FOR(i,n) for(i=0; i<n; ++i)
|
||||||
|
|
||||||
|
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
|
||||||
|
#define ROL(a,o) ((((u64)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<<j)-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
|
||||||
|
{
|
||||||
|
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
|
||||||
|
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
|
||||||
|
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
|
||||||
|
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) 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 <i>n</i> bits must be in the least significant bit positions
|
||||||
|
* and must be delimited with a bit 1 at position <i>n</i>
|
||||||
|
* (counting from 0=LSB to 7=MSB) and followed by bits 0
|
||||||
|
* from position <i>n</i>+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<uint64_t*> (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 <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
53
hash/keccak.hpp
Normal file
53
hash/keccak.hpp
Normal file
@ -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 <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __HASH_KECCAK_HPP
|
||||||
|
#define __HASH_KECCAK_HPP
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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<uint8_t, 200> 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
|
179
test/hash/keccak.cpp
Normal file
179
test/hash/keccak.cpp
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
#include "hash/keccak.hpp"
|
||||||
|
#include "debug.hpp"
|
||||||
|
#include "tap.hpp"
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
|
||||||
|
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 <size_t S>
|
||||||
|
static constexpr
|
||||||
|
std::array<uint8_t,S/2>
|
||||||
|
make_array (const char (&str)[S])
|
||||||
|
{
|
||||||
|
if (S < 1)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
static_assert (S % 2 == 1, "requires full bytes + null");
|
||||||
|
std::array<uint8_t,S/2> 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 <size_t S>
|
||||||
|
static
|
||||||
|
std::vector<uint8_t>
|
||||||
|
make_vector (const char (&str)[S]) {
|
||||||
|
const auto arr = make_array (str);
|
||||||
|
return std::vector<uint8_t> (arr.cbegin (), arr.cend ());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int, char**)
|
||||||
|
{
|
||||||
|
util::TAP::logger tap;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *msg;
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
std::array<uint8_t, 28> sha3_224;
|
||||||
|
std::array<uint8_t, 32> sha3_256;
|
||||||
|
std::array<uint8_t, 48> sha3_384;
|
||||||
|
std::array<uint8_t, 64> 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<uint8_t> (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<uint8_t, WIDTH/8> 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 ();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user