forked from wareck/node-multi-hashing
-
Notifications
You must be signed in to change notification settings - Fork 4
/
zr5.c
222 lines (190 loc) · 7.05 KB
/
zr5.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "zr5.h"
//#include "uint256.h"
// Note that data types are defined in "sha3/sph_types.h", included by each of these
#include "sha3/sph_blake.h"
#include "sha3/sph_groestl.h"
#include "sha3/sph_jh.h"
#include "sha3/sph_keccak.h"
#include "sha3/sph_skein.h"
#ifdef __cplusplus
extern "C" {
#endif
enum {KECCAK = -1, BLAKE, GROESTL, JH, SKEIN} ziftrAlgoIDs;
static inline uint32_t Reverse32(uint32_t n)
{
#if BYTE_ORDER == LITTLE_ENDIAN
return n << 24 | (n & 0x0000ff00) << 8 | (n & 0x00ff0000) >> 8 | n >> 24;
#else
return n;
#endif
}
void zr5_hash_512( uint8_t* input, uint8_t* output, uint32_t len )
{
//static const uint256 INT_MASK("0xFFFFFFFF");
//static unsigned char pblank[1];
//pblank[0] = 0;
// Pre-computed table of permutations
static const int arrOrder[][4] = {
{0, 1, 2, 3},
{0, 1, 3, 2},
{0, 2, 1, 3},
{0, 2, 3, 1},
{0, 3, 1, 2},
{0, 3, 2, 1},
{1, 0, 2, 3},
{1, 0, 3, 2},
{1, 2, 0, 3},
{1, 2, 3, 0},
{1, 3, 0, 2},
{1, 3, 2, 0},
{2, 0, 1, 3},
{2, 0, 3, 1},
{2, 1, 0, 3},
{2, 1, 3, 0},
{2, 3, 0, 1},
{2, 3, 1, 0},
{3, 0, 1, 2},
{3, 0, 2, 1},
{3, 1, 0, 2},
{3, 1, 2, 0},
{3, 2, 0, 1},
{3, 2, 1, 0}
};
uint8_t hash[5][64]; // buffers for 5 512bit (64 byte) hash outputs
sph_blake512_context ctx_blake; // context for a blake hash
sph_groestl512_context ctx_groestl; // context for a blake hash
sph_jh512_context ctx_jh; // context for a blake hash
sph_keccak512_context ctx_keccak; // context for a blake hash
sph_skein512_context ctx_skein; // context for a blake hash
// int shiftbits; // unused
unsigned int nOrder; // order in which to apply the hashing algos
unsigned int i = 0; // loop counter
unsigned int j = 0; // loop counter
// Represent uint512 values as arrays of bytes because we don't have a native uint512
uint8_t * pStart;
uint8_t * pPutResult;
size_t nSize = len; // length of input buffer in bytes
// initialize the buffers for hash results
for(i=0; i<5; i++) { for(j=0; j<64; j++) { hash[i][j] = 0; } }
// initialize the four types of hashes outside the loop
sph_keccak512_init(&ctx_keccak);
sph_blake512_init(&ctx_blake);
sph_groestl512_init(&ctx_groestl);
sph_jh512_init(&ctx_jh);
sph_skein512_init(&ctx_skein);
// Always start with a Keccak hash
sph_keccak512 (&ctx_keccak, input, len);
// and put its output into the first hash output buffer
sph_keccak512_close(&ctx_keccak, &(hash[0]));
// Output from the keccak is the input to the next hash algorithm.
// Calculate the order of the remaining hashes
// by taking least significant 32 bits of the first hash,
// treating that as an integer, which we divide modulo array size,
// giving us an index into the array of hashing orders
//nOrder = getinnerint(&hash[0], 0, sizeof(hash[0]) ) % ARRAYLEN(arrOrder);
uint32_t gls32 = getleastsig32((uint8_t *)&hash[0], 0);
nOrder = gls32 % ARRAYLEN(arrOrder);
// The output of each of the five hashes is 512bits = 64 bytes.
// Therefore, the input to the last four hashes is also 64 bytes.
nSize = 64;
// now apply the remaining hashes in the calculated order
// start loop counter at 0 so we start with keccak results
for (i = 0; i < 4; i++) {
pStart = (uint8_t *)(&hash[i]);
pPutResult = (uint8_t *)(&hash[i+1]);
// have the result of the last hash go straight into the output buffer
// in order to use one less buffer and avoid having to copy the last result
if (i == 3)
pPutResult = output;
// apply blake, groestl, jh, and skein in an order determined by the
// result of the keccak hash
switch (arrOrder[nOrder][i]) {
case BLAKE:
sph_blake512(&ctx_blake, pStart, nSize);
sph_blake512_close(&ctx_blake, pPutResult);
break;
case GROESTL:
sph_groestl512(&ctx_groestl, pStart, nSize);
sph_groestl512_close(&ctx_groestl, pPutResult);
break;
case JH:
sph_jh512(&ctx_jh, pStart, nSize);
sph_jh512_close(&ctx_jh, pPutResult);
break;
case SKEIN:
sph_skein512(&ctx_skein, pStart, nSize);
sph_skein512_close(&ctx_skein, pPutResult);
break;
default:
break;
}
}
return;
}
void zr5_hash( void* input, void* output, uint32_t len)
{
uint8_t * input512; // writeable copy of input
uint8_t output512[64]; // output of both zr5 hashes
uint32_t version; // writeable copy of version
uint32_t nPoK = 0; // integer copy of PoK state
static const unsigned int POK_BOOL_MASK = 0x00008000;
static const unsigned int POK_DATA_MASK = 0xFFFF0000;
#ifdef TEST_VERBOSELY
uint32_t i = 0; // generic loop counter
#endif // TEST_VERBOSELY
// copy the input buffer at input to a modifiable location at input512,
input512 = (uint8_t *)malloc(len); // allocate space for the copy
memcpy((uint8_t *)input512, (uint8_t *)input, len);
#ifdef TEST_VERBOSELY
printf("zr5_hash input:\n");
for (i=0; i<len; i++) { printf("%02x", input512[i]); }
printf("\n\n");
#endif // TEST_VERBOSELY
// store the version bytes
memcpy((uint8_t *)&version, (uint8_t *)input, 4);
// apply the first hash, yielding 512bits = 64 bytes
zr5_hash_512(input512, output512, len);
// Now begins Proof of Knowledge
//
// Pull the data from the result for the Proof of Knowledge
// (this is the 3rd and 4th of the first four bytes of the result)
memcpy(&nPoK, (uint8_t *)output512, 4); // yields big or little endian uint
// keep only the two least significant bytes
#if BYTE_ORDER == LITTLE_ENDIAN
nPoK &= 0xFFFF0000; // bytes 3&4 of big endian are 1&2 of little endian
#else
nPoK &= 0x0000FFFF; // bytes 1&2 of big endian are 3&4 of little endian
#endif
//
// PoK part 2:
// update the version variable with the masks and PoK value
// according to the Proof of Knowledge setting
version &= (~POK_BOOL_MASK);
version |= (POK_DATA_MASK & nPoK);
printf("new version field: %u\n", version);
// and now write it back out to our copy of the input buffer
memcpy((uint8_t *)input512, (uint8_t *)&version, 4);
// apply a second ZR5 hash of the modified input, 512 bits in and out,
// to the input modified with PoK. Length is still the original length
zr5_hash_512(input512, output512, len);
// copy the left-most 256 bits (32 bytes) of the last hash into the output buffer
memcpy((uint8_t *)output, (uint8_t *)output512, sizeof(output512)/2);
free(input512);
return;
}
// WARNING: This routine might work for little endian numbers!
uint32_t getleastsig32( uint8_t* buffer, unsigned int nIndex)
{
uint32_t * ptr = NULL;
uint32_t result;
ptr = (uint32_t *)buffer;
result = ptr[nIndex % sizeof(uint32_t)];
return(result);
}
#ifdef __cplusplus
}
#endif