1 // SPDX-License-Identifier: BSD-2-Clause
2 /* LibTomCrypt, modular cryptographic library -- Tom St Denis
3 *
4 * LibTomCrypt is a library that provides various cryptographic
5 * algorithms in a highly modular and flexible manner.
6 *
7 * The library is free for all purposes without any express
8 * guarantee it works.
9 */
10
11 /**
12 @file whirl.c
13 LTC_WHIRLPOOL (using their new sbox) hash function by Tom St Denis
14 */
15
16 #include "tomcrypt_private.h"
17
18 #ifdef LTC_WHIRLPOOL
19
20 const struct ltc_hash_descriptor whirlpool_desc =
21 {
22 "whirlpool",
23 11,
24 64,
25 64,
26
27 /* OID */
28 { 1, 0, 10118, 3, 0, 55 },
29 6,
30
31 &whirlpool_init,
32 &whirlpool_process,
33 &whirlpool_done,
34 &whirlpool_test,
35 NULL
36 };
37
38 /* the sboxes */
39 #define __LTC_WHIRLTAB_C__
40 #include "whirltab.c"
41
42 /* get a_{i,j} */
43 #define GB(a,i,j) ((a[(i) & 7] >> (8 * (j))) & 255)
44
45 /* shortcut macro to perform three functions at once */
46 #define theta_pi_gamma(a, i) \
47 (SB0(GB(a, i-0, 7)) ^ \
48 SB1(GB(a, i-1, 6)) ^ \
49 SB2(GB(a, i-2, 5)) ^ \
50 SB3(GB(a, i-3, 4)) ^ \
51 SB4(GB(a, i-4, 3)) ^ \
52 SB5(GB(a, i-5, 2)) ^ \
53 SB6(GB(a, i-6, 1)) ^ \
54 SB7(GB(a, i-7, 0)))
55
56 #ifdef LTC_CLEAN_STACK
_whirlpool_compress(hash_state * md,const unsigned char * buf)57 static int _whirlpool_compress(hash_state *md, const unsigned char *buf)
58 #else
59 static int whirlpool_compress(hash_state *md, const unsigned char *buf)
60 #endif
61 {
62 ulong64 K[2][8], T[3][8];
63 int x, y;
64
65 /* load the block/state */
66 for (x = 0; x < 8; x++) {
67 K[0][x] = md->whirlpool.state[x];
68
69 LOAD64H(T[0][x], buf + (8 * x));
70 T[2][x] = T[0][x];
71 T[0][x] ^= K[0][x];
72 }
73
74 /* do rounds 1..10 */
75 for (x = 0; x < 10; x += 2) {
76 /* odd round */
77 /* apply main transform to K[0] into K[1] */
78 for (y = 0; y < 8; y++) {
79 K[1][y] = theta_pi_gamma(K[0], y);
80 }
81 /* xor the constant */
82 K[1][0] ^= cont[x];
83
84 /* apply main transform to T[0] into T[1] */
85 for (y = 0; y < 8; y++) {
86 T[1][y] = theta_pi_gamma(T[0], y) ^ K[1][y];
87 }
88
89 /* even round */
90 /* apply main transform to K[1] into K[0] */
91 for (y = 0; y < 8; y++) {
92 K[0][y] = theta_pi_gamma(K[1], y);
93 }
94 /* xor the constant */
95 K[0][0] ^= cont[x+1];
96
97 /* apply main transform to T[1] into T[0] */
98 for (y = 0; y < 8; y++) {
99 T[0][y] = theta_pi_gamma(T[1], y) ^ K[0][y];
100 }
101 }
102
103 /* store state */
104 for (x = 0; x < 8; x++) {
105 md->whirlpool.state[x] ^= T[0][x] ^ T[2][x];
106 }
107
108 return CRYPT_OK;
109 }
110
111
112 #ifdef LTC_CLEAN_STACK
whirlpool_compress(hash_state * md,const unsigned char * buf)113 static int whirlpool_compress(hash_state *md, const unsigned char *buf)
114 {
115 int err;
116 err = _whirlpool_compress(md, buf);
117 burn_stack((5 * 8 * sizeof(ulong64)) + (2 * sizeof(int)));
118 return err;
119 }
120 #endif
121
122
123 /**
124 Initialize the hash state
125 @param md The hash state you wish to initialize
126 @return CRYPT_OK if successful
127 */
whirlpool_init(hash_state * md)128 int whirlpool_init(hash_state * md)
129 {
130 LTC_ARGCHK(md != NULL);
131 zeromem(&md->whirlpool, sizeof(md->whirlpool));
132 return CRYPT_OK;
133 }
134
135 /**
136 Process a block of memory though the hash
137 @param md The hash state
138 @param in The data to hash
139 @param inlen The length of the data (octets)
140 @return CRYPT_OK if successful
141 */
142 HASH_PROCESS(whirlpool_process, whirlpool_compress, whirlpool, 64)
143
144 /**
145 Terminate the hash to get the digest
146 @param md The hash state
147 @param out [out] The destination of the hash (64 bytes)
148 @return CRYPT_OK if successful
149 */
whirlpool_done(hash_state * md,unsigned char * out)150 int whirlpool_done(hash_state * md, unsigned char *out)
151 {
152 int i;
153
154 LTC_ARGCHK(md != NULL);
155 LTC_ARGCHK(out != NULL);
156
157 if (md->whirlpool.curlen >= sizeof(md->whirlpool.buf)) {
158 return CRYPT_INVALID_ARG;
159 }
160
161 /* increase the length of the message */
162 md->whirlpool.length += md->whirlpool.curlen * 8;
163
164 /* append the '1' bit */
165 md->whirlpool.buf[md->whirlpool.curlen++] = (unsigned char)0x80;
166
167 /* if the length is currently above 32 bytes we append zeros
168 * then compress. Then we can fall back to padding zeros and length
169 * encoding like normal.
170 */
171 if (md->whirlpool.curlen > 32) {
172 while (md->whirlpool.curlen < 64) {
173 md->whirlpool.buf[md->whirlpool.curlen++] = (unsigned char)0;
174 }
175 whirlpool_compress(md, md->whirlpool.buf);
176 md->whirlpool.curlen = 0;
177 }
178
179 /* pad upto 56 bytes of zeroes (should be 32 but we only support 64-bit lengths) */
180 while (md->whirlpool.curlen < 56) {
181 md->whirlpool.buf[md->whirlpool.curlen++] = (unsigned char)0;
182 }
183
184 /* store length */
185 STORE64H(md->whirlpool.length, md->whirlpool.buf+56);
186 whirlpool_compress(md, md->whirlpool.buf);
187
188 /* copy output */
189 for (i = 0; i < 8; i++) {
190 STORE64H(md->whirlpool.state[i], out+(8*i));
191 }
192 #ifdef LTC_CLEAN_STACK
193 zeromem(md, sizeof(*md));
194 #endif
195 return CRYPT_OK;
196 }
197
198 /**
199 Self-test the hash
200 @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
201 */
whirlpool_test(void)202 int whirlpool_test(void)
203 {
204 #ifndef LTC_TEST
205 return CRYPT_NOP;
206 #else
207 static const struct {
208 int len;
209 unsigned char msg[128], hash[64];
210 } tests[] = {
211
212 /* NULL Message */
213 {
214 0,
215 { 0x00 },
216 { 0x19, 0xFA, 0x61, 0xD7, 0x55, 0x22, 0xA4, 0x66, 0x9B, 0x44, 0xE3, 0x9C, 0x1D, 0x2E, 0x17, 0x26,
217 0xC5, 0x30, 0x23, 0x21, 0x30, 0xD4, 0x07, 0xF8, 0x9A, 0xFE, 0xE0, 0x96, 0x49, 0x97, 0xF7, 0xA7,
218 0x3E, 0x83, 0xBE, 0x69, 0x8B, 0x28, 0x8F, 0xEB, 0xCF, 0x88, 0xE3, 0xE0, 0x3C, 0x4F, 0x07, 0x57,
219 0xEA, 0x89, 0x64, 0xE5, 0x9B, 0x63, 0xD9, 0x37, 0x08, 0xB1, 0x38, 0xCC, 0x42, 0xA6, 0x6E, 0xB3 }
220 },
221
222
223 /* 448-bits of 0 bits */
224 {
225
226 56,
227 { 0x00 },
228 { 0x0B, 0x3F, 0x53, 0x78, 0xEB, 0xED, 0x2B, 0xF4, 0xD7, 0xBE, 0x3C, 0xFD, 0x81, 0x8C, 0x1B, 0x03,
229 0xB6, 0xBB, 0x03, 0xD3, 0x46, 0x94, 0x8B, 0x04, 0xF4, 0xF4, 0x0C, 0x72, 0x6F, 0x07, 0x58, 0x70,
230 0x2A, 0x0F, 0x1E, 0x22, 0x58, 0x80, 0xE3, 0x8D, 0xD5, 0xF6, 0xED, 0x6D, 0xE9, 0xB1, 0xE9, 0x61,
231 0xE4, 0x9F, 0xC1, 0x31, 0x8D, 0x7C, 0xB7, 0x48, 0x22, 0xF3, 0xD0, 0xE2, 0xE9, 0xA7, 0xE7, 0xB0 }
232 },
233
234 /* 520-bits of 0 bits */
235 {
236 65,
237 { 0x00 },
238 { 0x85, 0xE1, 0x24, 0xC4, 0x41, 0x5B, 0xCF, 0x43, 0x19, 0x54, 0x3E, 0x3A, 0x63, 0xFF, 0x57, 0x1D,
239 0x09, 0x35, 0x4C, 0xEE, 0xBE, 0xE1, 0xE3, 0x25, 0x30, 0x8C, 0x90, 0x69, 0xF4, 0x3E, 0x2A, 0xE4,
240 0xD0, 0xE5, 0x1D, 0x4E, 0xB1, 0xE8, 0x64, 0x28, 0x70, 0x19, 0x4E, 0x95, 0x30, 0xD8, 0xD8, 0xAF,
241 0x65, 0x89, 0xD1, 0xBF, 0x69, 0x49, 0xDD, 0xF9, 0x0A, 0x7F, 0x12, 0x08, 0x62, 0x37, 0x95, 0xB9 }
242 },
243
244 /* 512-bits, leading set */
245 {
246 64,
247 { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
251 { 0x10, 0x3E, 0x00, 0x55, 0xA9, 0xB0, 0x90, 0xE1, 0x1C, 0x8F, 0xDD, 0xEB, 0xBA, 0x06, 0xC0, 0x5A,
252 0xCE, 0x8B, 0x64, 0xB8, 0x96, 0x12, 0x8F, 0x6E, 0xED, 0x30, 0x71, 0xFC, 0xF3, 0xDC, 0x16, 0x94,
253 0x67, 0x78, 0xE0, 0x72, 0x23, 0x23, 0x3F, 0xD1, 0x80, 0xFC, 0x40, 0xCC, 0xDB, 0x84, 0x30, 0xA6,
254 0x40, 0xE3, 0x76, 0x34, 0x27, 0x1E, 0x65, 0x5C, 0xA1, 0x67, 0x4E, 0xBF, 0xF5, 0x07, 0xF8, 0xCB }
255 },
256
257 /* 512-bits, leading set of second byte */
258 {
259 64,
260 { 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
261 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
264 { 0x35, 0x7B, 0x42, 0xEA, 0x79, 0xBC, 0x97, 0x86, 0x97, 0x5A, 0x3C, 0x44, 0x70, 0xAA, 0xB2, 0x3E,
265 0x62, 0x29, 0x79, 0x7B, 0xAD, 0xBD, 0x54, 0x36, 0x5B, 0x54, 0x96, 0xE5, 0x5D, 0x9D, 0xD7, 0x9F,
266 0xE9, 0x62, 0x4F, 0xB4, 0x22, 0x66, 0x93, 0x0A, 0x62, 0x8E, 0xD4, 0xDB, 0x08, 0xF9, 0xDD, 0x35,
267 0xEF, 0x1B, 0xE1, 0x04, 0x53, 0xFC, 0x18, 0xF4, 0x2C, 0x7F, 0x5E, 0x1F, 0x9B, 0xAE, 0x55, 0xE0 }
268 },
269
270 /* 512-bits, leading set of last byte */
271 {
272 64,
273 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
275 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 },
277 { 0x8B, 0x39, 0x04, 0xDD, 0x19, 0x81, 0x41, 0x26, 0xFD, 0x02, 0x74, 0xAB, 0x49, 0xC5, 0x97, 0xF6,
278 0xD7, 0x75, 0x33, 0x52, 0xA2, 0xDD, 0x91, 0xFD, 0x8F, 0x9F, 0x54, 0x05, 0x4C, 0x54, 0xBF, 0x0F,
279 0x06, 0xDB, 0x4F, 0xF7, 0x08, 0xA3, 0xA2, 0x8B, 0xC3, 0x7A, 0x92, 0x1E, 0xEE, 0x11, 0xED, 0x7B,
280 0x6A, 0x53, 0x79, 0x32, 0xCC, 0x5E, 0x94, 0xEE, 0x1E, 0xA6, 0x57, 0x60, 0x7E, 0x36, 0xC9, 0xF7 }
281 },
282
283 };
284
285 int i;
286 unsigned char tmp[64];
287 hash_state md;
288
289 for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
290 whirlpool_init(&md);
291 whirlpool_process(&md, (unsigned char *)tests[i].msg, tests[i].len);
292 whirlpool_done(&md, tmp);
293 if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "WHIRLPOOL", i)) {
294 return CRYPT_FAIL_TESTVECTOR;
295 }
296 }
297 return CRYPT_OK;
298 #endif
299 }
300
301
302 #endif
303
304
305 /* ref: $Format:%D$ */
306 /* git commit: $Format:%H$ */
307 /* commit time: $Format:%ai$ */
308