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 BLAKE2 reference source code package - reference C implementations
13
14 Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under the
15 terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
16 your option. The terms of these licenses can be found at:
17
18 - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
19 - OpenSSL license : https://www.openssl.org/source/license.html
20 - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0
21
22 More information about the BLAKE2 hash function can be found at
23 https://blake2.net.
24 */
25 /* see also https://www.ietf.org/rfc/rfc7693.txt */
26
27 #include "tomcrypt_private.h"
28
29 #ifdef LTC_BLAKE2S
30
31 enum blake2s_constant {
32 BLAKE2S_BLOCKBYTES = 64,
33 BLAKE2S_OUTBYTES = 32,
34 BLAKE2S_KEYBYTES = 32,
35 BLAKE2S_SALTBYTES = 8,
36 BLAKE2S_PERSONALBYTES = 8,
37 BLAKE2S_PARAM_SIZE = 32
38 };
39
40 /* param offsets */
41 enum {
42 O_DIGEST_LENGTH = 0,
43 O_KEY_LENGTH = 1,
44 O_FANOUT = 2,
45 O_DEPTH = 3,
46 O_LEAF_LENGTH = 4,
47 O_NODE_OFFSET = 8,
48 O_XOF_LENGTH = 12,
49 O_NODE_DEPTH = 14,
50 O_INNER_LENGTH = 15,
51 O_SALT = 16,
52 O_PERSONAL = 24
53 };
54
55 /*
56 struct blake2s_param {
57 unsigned char digest_length;
58 unsigned char key_length;
59 unsigned char fanout;
60 unsigned char depth;
61 ulong32 leaf_length;
62 ulong32 node_offset;
63 ushort16 xof_length;
64 unsigned char node_depth;
65 unsigned char inner_length;
66 unsigned char salt[BLAKE2S_SALTBYTES];
67 unsigned char personal[BLAKE2S_PERSONALBYTES];
68 };
69 */
70
71 const struct ltc_hash_descriptor blake2s_128_desc =
72 {
73 "blake2s-128",
74 21,
75 16,
76 64,
77 { 1, 3, 6, 1, 4, 1, 1722, 12, 2, 2, 4 },
78 11,
79 &blake2s_128_init,
80 &blake2s_process,
81 &blake2s_done,
82 &blake2s_128_test,
83 NULL
84 };
85
86 const struct ltc_hash_descriptor blake2s_160_desc =
87 {
88 "blake2s-160",
89 22,
90 20,
91 64,
92 { 1, 3, 6, 1, 4, 1, 1722, 12, 2, 2, 5 },
93 11,
94 &blake2s_160_init,
95 &blake2s_process,
96 &blake2s_done,
97 &blake2s_160_test,
98 NULL
99 };
100
101 const struct ltc_hash_descriptor blake2s_224_desc =
102 {
103 "blake2s-224",
104 23,
105 28,
106 64,
107 { 1, 3, 6, 1, 4, 1, 1722, 12, 2, 2, 7 },
108 11,
109 &blake2s_224_init,
110 &blake2s_process,
111 &blake2s_done,
112 &blake2s_224_test,
113 NULL
114 };
115
116 const struct ltc_hash_descriptor blake2s_256_desc =
117 {
118 "blake2s-256",
119 24,
120 32,
121 64,
122 { 1, 3, 6, 1, 4, 1, 1722, 12, 2, 2, 8 },
123 11,
124 &blake2s_256_init,
125 &blake2s_process,
126 &blake2s_done,
127 &blake2s_256_test,
128 NULL
129 };
130
131 static const ulong32 blake2s_IV[8] = {
132 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
133 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
134 };
135
136 static const unsigned char blake2s_sigma[10][16] = {
137 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
138 { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
139 { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
140 { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
141 { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
142 { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
143 { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
144 { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
145 { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
146 { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
147 };
148
blake2s_set_lastnode(hash_state * md)149 static void blake2s_set_lastnode(hash_state *md) { md->blake2s.f[1] = 0xffffffffUL; }
150
151 /* Some helper functions, not necessarily useful */
blake2s_is_lastblock(const hash_state * md)152 static int blake2s_is_lastblock(const hash_state *md) { return md->blake2s.f[0] != 0; }
153
blake2s_set_lastblock(hash_state * md)154 static void blake2s_set_lastblock(hash_state *md)
155 {
156 if (md->blake2s.last_node) {
157 blake2s_set_lastnode(md);
158 }
159 md->blake2s.f[0] = 0xffffffffUL;
160 }
161
blake2s_increment_counter(hash_state * md,const ulong32 inc)162 static void blake2s_increment_counter(hash_state *md, const ulong32 inc)
163 {
164 md->blake2s.t[0] += inc;
165 if (md->blake2s.t[0] < inc) md->blake2s.t[1]++;
166 }
167
blake2s_init0(hash_state * md)168 static int blake2s_init0(hash_state *md)
169 {
170 int i;
171 XMEMSET(&md->blake2s, 0, sizeof(struct blake2s_state));
172
173 for (i = 0; i < 8; ++i) {
174 md->blake2s.h[i] = blake2s_IV[i];
175 }
176
177 return CRYPT_OK;
178 }
179
180 /* init2 xors IV with input parameter block */
blake2s_init_param(hash_state * md,const unsigned char * P)181 static int blake2s_init_param(hash_state *md, const unsigned char *P)
182 {
183 unsigned long i;
184
185 blake2s_init0(md);
186
187 /* IV XOR ParamBlock */
188 for (i = 0; i < 8; ++i) {
189 ulong32 tmp;
190 LOAD32L(tmp, P + i * 4);
191 md->blake2s.h[i] ^= tmp;
192 }
193
194 md->blake2s.outlen = P[O_DIGEST_LENGTH];
195 return CRYPT_OK;
196 }
197
198 /**
199 Initialize the hash/MAC state
200
201 Use this function to init for arbitrary sizes.
202
203 Give a key and keylen to init for MAC mode.
204
205 @param md The hash state you wish to initialize
206 @param outlen The desired output-length
207 @param key The key of the MAC
208 @param keylen The length of the key
209 @return CRYPT_OK if successful
210 */
blake2s_init(hash_state * md,unsigned long outlen,const unsigned char * key,unsigned long keylen)211 int blake2s_init(hash_state *md, unsigned long outlen, const unsigned char *key, unsigned long keylen)
212 {
213 unsigned char P[BLAKE2S_PARAM_SIZE];
214 int err;
215
216 LTC_ARGCHK(md != NULL);
217
218 if ((!outlen) || (outlen > BLAKE2S_OUTBYTES)) {
219 return CRYPT_INVALID_ARG;
220 }
221 if ((key && !keylen) || (keylen && !key) || (keylen > BLAKE2S_KEYBYTES)) {
222 return CRYPT_INVALID_ARG;
223 }
224
225 XMEMSET(P, 0, sizeof(P));
226
227 P[O_DIGEST_LENGTH] = (unsigned char)outlen;
228 P[O_KEY_LENGTH] = (unsigned char)keylen;
229 P[O_FANOUT] = 1;
230 P[O_DEPTH] = 1;
231
232 err = blake2s_init_param(md, P);
233 if (err != CRYPT_OK) return err;
234
235 if (key) {
236 unsigned char block[BLAKE2S_BLOCKBYTES];
237
238 XMEMSET(block, 0, BLAKE2S_BLOCKBYTES);
239 XMEMCPY(block, key, keylen);
240 blake2s_process(md, block, BLAKE2S_BLOCKBYTES);
241
242 #ifdef LTC_CLEAN_STACK
243 zeromem(block, sizeof(block));
244 #endif
245 }
246 return CRYPT_OK;
247 }
248
249 /**
250 Initialize the hash state
251 @param md The hash state you wish to initialize
252 @return CRYPT_OK if successful
253 */
blake2s_128_init(hash_state * md)254 int blake2s_128_init(hash_state *md) { return blake2s_init(md, 16, NULL, 0); }
255
256 /**
257 Initialize the hash state
258 @param md The hash state you wish to initialize
259 @return CRYPT_OK if successful
260 */
blake2s_160_init(hash_state * md)261 int blake2s_160_init(hash_state *md) { return blake2s_init(md, 20, NULL, 0); }
262
263 /**
264 Initialize the hash state
265 @param md The hash state you wish to initialize
266 @return CRYPT_OK if successful
267 */
blake2s_224_init(hash_state * md)268 int blake2s_224_init(hash_state *md) { return blake2s_init(md, 28, NULL, 0); }
269
270 /**
271 Initialize the hash state
272 @param md The hash state you wish to initialize
273 @return CRYPT_OK if successful
274 */
blake2s_256_init(hash_state * md)275 int blake2s_256_init(hash_state *md) { return blake2s_init(md, 32, NULL, 0); }
276
277 #define G(r, i, a, b, c, d) \
278 do { \
279 a = a + b + m[blake2s_sigma[r][2 * i + 0]]; \
280 d = ROR(d ^ a, 16); \
281 c = c + d; \
282 b = ROR(b ^ c, 12); \
283 a = a + b + m[blake2s_sigma[r][2 * i + 1]]; \
284 d = ROR(d ^ a, 8); \
285 c = c + d; \
286 b = ROR(b ^ c, 7); \
287 } while (0)
288 #define ROUND(r) \
289 do { \
290 G(r, 0, v[0], v[4], v[8], v[12]); \
291 G(r, 1, v[1], v[5], v[9], v[13]); \
292 G(r, 2, v[2], v[6], v[10], v[14]); \
293 G(r, 3, v[3], v[7], v[11], v[15]); \
294 G(r, 4, v[0], v[5], v[10], v[15]); \
295 G(r, 5, v[1], v[6], v[11], v[12]); \
296 G(r, 6, v[2], v[7], v[8], v[13]); \
297 G(r, 7, v[3], v[4], v[9], v[14]); \
298 } while (0)
299
300 #ifdef LTC_CLEAN_STACK
_blake2s_compress(hash_state * md,const unsigned char * buf)301 static int _blake2s_compress(hash_state *md, const unsigned char *buf)
302 #else
303 static int blake2s_compress(hash_state *md, const unsigned char *buf)
304 #endif
305 {
306 unsigned long i;
307 ulong32 m[16];
308 ulong32 v[16];
309
310 for (i = 0; i < 16; ++i) {
311 LOAD32L(m[i], buf + i * sizeof(m[i]));
312 }
313
314 for (i = 0; i < 8; ++i) {
315 v[i] = md->blake2s.h[i];
316 }
317
318 v[8] = blake2s_IV[0];
319 v[9] = blake2s_IV[1];
320 v[10] = blake2s_IV[2];
321 v[11] = blake2s_IV[3];
322 v[12] = md->blake2s.t[0] ^ blake2s_IV[4];
323 v[13] = md->blake2s.t[1] ^ blake2s_IV[5];
324 v[14] = md->blake2s.f[0] ^ blake2s_IV[6];
325 v[15] = md->blake2s.f[1] ^ blake2s_IV[7];
326
327 ROUND(0);
328 ROUND(1);
329 ROUND(2);
330 ROUND(3);
331 ROUND(4);
332 ROUND(5);
333 ROUND(6);
334 ROUND(7);
335 ROUND(8);
336 ROUND(9);
337
338 for (i = 0; i < 8; ++i) {
339 md->blake2s.h[i] = md->blake2s.h[i] ^ v[i] ^ v[i + 8];
340 }
341 return CRYPT_OK;
342 }
343 #undef G
344 #undef ROUND
345
346 #ifdef LTC_CLEAN_STACK
blake2s_compress(hash_state * md,const unsigned char * buf)347 static int blake2s_compress(hash_state *md, const unsigned char *buf)
348 {
349 int err;
350 err = _blake2s_compress(md, buf);
351 burn_stack(sizeof(ulong32) * (32) + sizeof(unsigned long));
352 return err;
353 }
354 #endif
355
356 /**
357 Process a block of memory through the hash
358 @param md The hash state
359 @param in The data to hash
360 @param inlen The length of the data (octets)
361 @return CRYPT_OK if successful
362 */
blake2s_process(hash_state * md,const unsigned char * in,unsigned long inlen)363 int blake2s_process(hash_state *md, const unsigned char *in, unsigned long inlen)
364 {
365 LTC_ARGCHK(md != NULL);
366 LTC_ARGCHK(in != NULL);
367
368 if (md->blake2s.curlen > sizeof(md->blake2s.buf)) {
369 return CRYPT_INVALID_ARG;
370 }
371
372 if (inlen > 0) {
373 unsigned long left = md->blake2s.curlen;
374 unsigned long fill = BLAKE2S_BLOCKBYTES - left;
375 if (inlen > fill) {
376 md->blake2s.curlen = 0;
377 XMEMCPY(md->blake2s.buf + (left % sizeof(md->blake2s.buf)), in, fill); /* Fill buffer */
378 blake2s_increment_counter(md, BLAKE2S_BLOCKBYTES);
379 blake2s_compress(md, md->blake2s.buf); /* Compress */
380 in += fill;
381 inlen -= fill;
382 while (inlen > BLAKE2S_BLOCKBYTES) {
383 blake2s_increment_counter(md, BLAKE2S_BLOCKBYTES);
384 blake2s_compress(md, in);
385 in += BLAKE2S_BLOCKBYTES;
386 inlen -= BLAKE2S_BLOCKBYTES;
387 }
388 }
389 XMEMCPY(md->blake2s.buf + md->blake2s.curlen, in, inlen);
390 md->blake2s.curlen += inlen;
391 }
392 return CRYPT_OK;
393 }
394
395 /**
396 Terminate the hash to get the digest
397 @param md The hash state
398 @param out [out] The destination of the hash (size depending on the length used on init)
399 @return CRYPT_OK if successful
400 */
blake2s_done(hash_state * md,unsigned char * out)401 int blake2s_done(hash_state *md, unsigned char *out)
402 {
403 unsigned char buffer[BLAKE2S_OUTBYTES] = { 0 };
404 unsigned long i;
405
406 LTC_ARGCHK(md != NULL);
407 LTC_ARGCHK(out != NULL);
408
409 /* if(md->blake2s.outlen != outlen) return CRYPT_INVALID_ARG; */
410
411 if (blake2s_is_lastblock(md)) {
412 return CRYPT_ERROR;
413 }
414 blake2s_increment_counter(md, md->blake2s.curlen);
415 blake2s_set_lastblock(md);
416 XMEMSET(md->blake2s.buf + md->blake2s.curlen, 0, BLAKE2S_BLOCKBYTES - md->blake2s.curlen); /* Padding */
417 blake2s_compress(md, md->blake2s.buf);
418
419 for (i = 0; i < 8; ++i) { /* Output full hash to temp buffer */
420 STORE32L(md->blake2s.h[i], buffer + i * 4);
421 }
422
423 XMEMCPY(out, buffer, md->blake2s.outlen);
424 zeromem(md, sizeof(hash_state));
425 #ifdef LTC_CLEAN_STACK
426 zeromem(buffer, sizeof(buffer));
427 #endif
428 return CRYPT_OK;
429 }
430
431 /**
432 Self-test the hash
433 @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
434 */
blake2s_256_test(void)435 int blake2s_256_test(void)
436 {
437 #ifndef LTC_TEST
438 return CRYPT_NOP;
439 #else
440 static const struct {
441 const char *msg;
442 unsigned char hash[32];
443 } tests[] = {
444 { "",
445 { 0x69, 0x21, 0x7a, 0x30, 0x79, 0x90, 0x80, 0x94,
446 0xe1, 0x11, 0x21, 0xd0, 0x42, 0x35, 0x4a, 0x7c,
447 0x1f, 0x55, 0xb6, 0x48, 0x2c, 0xa1, 0xa5, 0x1e,
448 0x1b, 0x25, 0x0d, 0xfd, 0x1e, 0xd0, 0xee, 0xf9 } },
449 { "abc",
450 { 0x50, 0x8c, 0x5e, 0x8c, 0x32, 0x7c, 0x14, 0xe2,
451 0xe1, 0xa7, 0x2b, 0xa3, 0x4e, 0xeb, 0x45, 0x2f,
452 0x37, 0x45, 0x8b, 0x20, 0x9e, 0xd6, 0x3a, 0x29,
453 0x4d, 0x99, 0x9b, 0x4c, 0x86, 0x67, 0x59, 0x82 } },
454 { "12345678901234567890123456789012345678901234567890"
455 "12345678901234567890123456789012345678901234567890"
456 "12345678901234567890123456789012345678901234567890"
457 "12345678901234567890123456789012345678901234567890"
458 "12345678901234567890123456789012345678901234567890"
459 "12345678901234567890123456789012345678901234567890",
460 { 0xa3, 0x78, 0x8b, 0x5b, 0x59, 0xee, 0xe4, 0x41,
461 0x95, 0x23, 0x58, 0x00, 0xa4, 0xf9, 0xfa, 0x41,
462 0x86, 0x0c, 0x7b, 0x1c, 0x35, 0xa2, 0x42, 0x70,
463 0x50, 0x80, 0x79, 0x56, 0xe3, 0xbe, 0x31, 0x74 } },
464
465 { NULL, { 0 } }
466 };
467
468 int i;
469 unsigned char tmp[32];
470 hash_state md;
471
472 for (i = 0; tests[i].msg != NULL; i++) {
473 blake2s_256_init(&md);
474 blake2s_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
475 blake2s_done(&md, tmp);
476 if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "BLAKE2S_256", i)) {
477 return CRYPT_FAIL_TESTVECTOR;
478 }
479
480 }
481 return CRYPT_OK;
482 #endif
483 }
484
485 /**
486 Self-test the hash
487 @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
488 */
blake2s_224_test(void)489 int blake2s_224_test(void)
490 {
491 #ifndef LTC_TEST
492 return CRYPT_NOP;
493 #else
494 static const struct {
495 const char *msg;
496 unsigned char hash[28];
497 } tests[] = {
498 { "",
499 { 0x1f, 0xa1, 0x29, 0x1e, 0x65, 0x24, 0x8b, 0x37,
500 0xb3, 0x43, 0x34, 0x75, 0xb2, 0xa0, 0xdd, 0x63,
501 0xd5, 0x4a, 0x11, 0xec, 0xc4, 0xe3, 0xe0, 0x34,
502 0xe7, 0xbc, 0x1e, 0xf4 } },
503 { "abc",
504 { 0x0b, 0x03, 0x3f, 0xc2, 0x26, 0xdf, 0x7a, 0xbd,
505 0xe2, 0x9f, 0x67, 0xa0, 0x5d, 0x3d, 0xc6, 0x2c,
506 0xf2, 0x71, 0xef, 0x3d, 0xfe, 0xa4, 0xd3, 0x87,
507 0x40, 0x7f, 0xbd, 0x55 } },
508
509 { NULL, { 0 } }
510 };
511
512 int i;
513 unsigned char tmp[28];
514 hash_state md;
515
516 for (i = 0; tests[i].msg != NULL; i++) {
517 blake2s_224_init(&md);
518 blake2s_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
519 blake2s_done(&md, tmp);
520 if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "BLAKE2S_224", i)) {
521 return CRYPT_FAIL_TESTVECTOR;
522 }
523
524 }
525 return CRYPT_OK;
526 #endif
527 }
528
529 /**
530 Self-test the hash
531 @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
532 */
blake2s_160_test(void)533 int blake2s_160_test(void)
534 {
535 #ifndef LTC_TEST
536 return CRYPT_NOP;
537 #else
538 static const struct {
539 const char *msg;
540 unsigned char hash[20];
541 } tests[] = {
542 { "",
543 { 0x35, 0x4c, 0x9c, 0x33, 0xf7, 0x35, 0x96, 0x24,
544 0x18, 0xbd, 0xac, 0xb9, 0x47, 0x98, 0x73, 0x42,
545 0x9c, 0x34, 0x91, 0x6f} },
546 { "abc",
547 { 0x5a, 0xe3, 0xb9, 0x9b, 0xe2, 0x9b, 0x01, 0x83,
548 0x4c, 0x3b, 0x50, 0x85, 0x21, 0xed, 0xe6, 0x04,
549 0x38, 0xf8, 0xde, 0x17 } },
550
551 { NULL, { 0 } }
552 };
553
554 int i;
555 unsigned char tmp[20];
556 hash_state md;
557
558 for (i = 0; tests[i].msg != NULL; i++) {
559 blake2s_160_init(&md);
560 blake2s_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
561 blake2s_done(&md, tmp);
562 if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "BLAKE2S_160", i)) {
563 return CRYPT_FAIL_TESTVECTOR;
564 }
565
566 }
567 return CRYPT_OK;
568 #endif
569 }
570
571 /**
572 Self-test the hash
573 @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
574 */
blake2s_128_test(void)575 int blake2s_128_test(void)
576 {
577 #ifndef LTC_TEST
578 return CRYPT_NOP;
579 #else
580 static const struct {
581 const char *msg;
582 unsigned char hash[16];
583 } tests[] = {
584 { "",
585 { 0x64, 0x55, 0x0d, 0x6f, 0xfe, 0x2c, 0x0a, 0x01,
586 0xa1, 0x4a, 0xba, 0x1e, 0xad, 0xe0, 0x20, 0x0c } },
587 { "abc",
588 { 0xaa, 0x49, 0x38, 0x11, 0x9b, 0x1d, 0xc7, 0xb8,
589 0x7c, 0xba, 0xd0, 0xff, 0xd2, 0x00, 0xd0, 0xae } },
590
591 { NULL, { 0 } }
592 };
593
594 int i;
595 unsigned char tmp[16];
596 hash_state md;
597
598 for (i = 0; tests[i].msg != NULL; i++) {
599 blake2s_128_init(&md);
600 blake2s_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg));
601 blake2s_done(&md, tmp);
602 if (compare_testvector(tmp, sizeof(tmp), tests[i].hash, sizeof(tests[i].hash), "BLAKE2S_128", i)) {
603 return CRYPT_FAIL_TESTVECTOR;
604 }
605 }
606 return CRYPT_OK;
607 #endif
608 }
609
610 #endif
611
612 /* ref: $Format:%D$ */
613 /* git commit: $Format:%H$ */
614 /* commit time: $Format:%ai$ */
615