OSDN Git Service

ad5c3300bc38082d1a7b69cf81c4a9e0763ff6dc
[mhash384/mhash384.git] / frontend / src / self_test.cpp
1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Simple fast portable secure hashing library                                        */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de>                                            */
4 /*                                                                                                */
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software  */
6 /* and associated documentation files (the "Software"), to deal in the Software without           */
7 /* restriction, including without limitation the rights to use, copy, modify, merge, publish,     */
8 /* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the  */
9 /* Software is furnished to do so, subject to the following conditions:                           */
10 /*                                                                                                */
11 /* The above copyright notice and this permission notice shall be included in all copies or       */
12 /* substantial portions of the Software.                                                          */
13 /*                                                                                                */
14 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  */
15 /* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     */
16 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   */
17 /* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
18 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        */
19 /* ---------------------------------------------------------------------------------------------- */
20
21 #include "self_test.h"
22 #include "utils.h"
23 #include "mhash384.h"
24
25 #include <cstring>
26 #include <unordered_set>
27 #include <array>
28 #include <algorithm>
29
30 /*
31  * Test-case specification
32  */
33 typedef struct _test_case_t
34 {
35         uint32_t count;
36         const char *string;
37 }
38 test_case_t;
39
40 /*
41  * Pre-defined test-cases
42  */
43 static const test_case_t SELFTEST_INPUT[] =
44 {
45         { 0x00000001, "" },
46         { 0x00000001, "abc" },
47         { 0x00000001, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" },
48         { 0x00000001, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" },
49         { 0x000186A0, "aaaaaaaaaa" },
50         { 0x01000000, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" },
51         { 0x00000001, "The quick brown fox jumps over the lazy dog" },
52         { 0x00000001, "The quick brown fox jumps over the lazy cog" },
53         { 0x00000001, "Franz jagt im komplett verwahrlosten Taxi quer durch Bayern" },
54         { 0x00000001, "Frank jagt im komplett verwahrlosten Taxi quer durch Bayern" },
55         { 0x00000001, "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." },
56         { 0x00000001, "Lorem ipsum dolor sit amet, consectetur adipisici elit, sed eiusmod tempor incidunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamc0 laboris nisi ut aliquid ex ea commodi consequat. Quis aute iure reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint obcaecat cupiditat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." },
57         { 0x00000000, NULL} /*EOL*/
58 };
59
60 /*
61  * Expected hash values
62  */
63 static const uint8_t SELFTEST_EXPECTED[][MHASH384_SIZE] =
64 {
65         { 0x4C, 0x4B, 0x82, 0xD0, 0x7B, 0x36, 0x8E, 0x1C, 0x22, 0xD0, 0xDE, 0x37, 0x59, 0xC3, 0x2D, 0x44, 0xDA, 0x71, 0xBE, 0x62, 0x83, 0xE8, 0x55, 0x0A, 0x24, 0x68, 0xDC, 0x1F, 0xEC, 0x38, 0x91, 0x9F, 0x7E, 0xDB, 0x6C, 0x1B, 0xA0, 0x83, 0x78, 0xEC, 0x58, 0x3A, 0xE6, 0x12, 0xAB, 0x0E, 0x02, 0xBA },
66         { 0x91, 0x71, 0xD8, 0x3E, 0xE7, 0xDE, 0xDE, 0x36, 0xCA, 0xF2, 0x7C, 0x26, 0x44, 0x89, 0x7F, 0x31, 0x14, 0xA0, 0xF6, 0x7B, 0x6E, 0x91, 0x93, 0xAA, 0x1A, 0xB2, 0x34, 0x62, 0xEA, 0x81, 0x5E, 0xDE, 0xA5, 0x35, 0x00, 0x26, 0x71, 0xE0, 0x86, 0x49, 0x3B, 0x41, 0xA5, 0x28, 0xA2, 0x6F, 0xD8, 0xB3 },
67         { 0x29, 0x0B, 0xD2, 0x16, 0x2C, 0x21, 0x05, 0xA0, 0x82, 0x41, 0x72, 0xA8, 0x87, 0x5E, 0xE3, 0x3B, 0xB6, 0x5A, 0x98, 0xDC, 0x09, 0x28, 0x10, 0x04, 0x41, 0xB4, 0x1B, 0x93, 0x99, 0xF6, 0xA8, 0xEA, 0x09, 0x79, 0x48, 0x34, 0x50, 0x4A, 0x3E, 0x81, 0x7D, 0x49, 0xD2, 0x9B, 0xC2, 0x0A, 0x52, 0x0A },
68         { 0x0B, 0x3F, 0x13, 0xA6, 0x8A, 0xA8, 0xD8, 0xF0, 0xC5, 0xB9, 0xBF, 0x8B, 0xE5, 0xAE, 0xCC, 0xB7, 0x3E, 0x0D, 0x13, 0x73, 0x2C, 0x92, 0x90, 0x00, 0x6B, 0x6D, 0xC9, 0x39, 0xAD, 0xA7, 0x9C, 0x48, 0xAE, 0x36, 0x2E, 0x54, 0x5A, 0x06, 0x7D, 0x2C, 0x1F, 0xB0, 0x74, 0x9C, 0x60, 0xA4, 0x92, 0x43 },
69         { 0x56, 0x22, 0x8E, 0x94, 0x32, 0x47, 0x1B, 0x09, 0xA7, 0xF6, 0x96, 0xD0, 0xDE, 0xFA, 0x15, 0xE6, 0x64, 0xD3, 0xE7, 0xAC, 0xD2, 0x7E, 0x2D, 0x39, 0xF8, 0x64, 0xC0, 0x50, 0x06, 0xF1, 0xF7, 0x70, 0x12, 0xF4, 0xF4, 0xCC, 0xE7, 0x45, 0x0C, 0x52, 0xB6, 0xC1, 0xCF, 0xAB, 0x84, 0xFA, 0xEC, 0x63 },
70         { 0x3A, 0x19, 0x9A, 0x67, 0x3F, 0xAB, 0x29, 0x00, 0xAB, 0x80, 0xFE, 0xC1, 0x18, 0x5F, 0x79, 0x35, 0x9F, 0xEC, 0x44, 0xB8, 0x87, 0x28, 0xE3, 0xD6, 0x2D, 0xC3, 0x1A, 0x93, 0x6C, 0x62, 0xDB, 0x05, 0xEF, 0x35, 0x71, 0x6F, 0xED, 0x30, 0x74, 0xE9, 0x31, 0x0D, 0xDD, 0xF6, 0x9E, 0xD5, 0x67, 0x1C },
71         { 0x79, 0xF7, 0x6C, 0xA5, 0x3D, 0x52, 0x91, 0x62, 0xE6, 0x32, 0x15, 0x2E, 0xDE, 0x82, 0xA4, 0x03, 0xF8, 0xF9, 0x96, 0xDE, 0xAA, 0x00, 0x9C, 0xC5, 0x12, 0x25, 0x0B, 0xAF, 0xF9, 0x10, 0xAC, 0x24, 0xDF, 0x13, 0x81, 0xF7, 0xEF, 0x1F, 0x43, 0xDA, 0xC2, 0x6F, 0x63, 0xEE, 0x0C, 0xFF, 0x3C, 0xDF },
72         { 0x8A, 0x2A, 0x58, 0xB2, 0x00, 0x20, 0xF7, 0x70, 0x0F, 0xFF, 0x62, 0x9B, 0x0D, 0x72, 0x38, 0xD3, 0xD5, 0x31, 0x1A, 0xC2, 0xA9, 0xAD, 0xA6, 0x06, 0xE6, 0x9A, 0xD7, 0xBE, 0xBF, 0x2B, 0x62, 0x58, 0xAE, 0xC7, 0x40, 0x80, 0xDE, 0xC0, 0x4A, 0xD5, 0x9F, 0x3B, 0x93, 0x26, 0x12, 0x1D, 0xFF, 0x66 },
73         { 0xD2, 0xE0, 0x7E, 0xA3, 0x7E, 0xF1, 0xE0, 0xE5, 0x2B, 0xB7, 0x04, 0xDE, 0xC3, 0x33, 0x0C, 0x33, 0x78, 0xB9, 0x43, 0xFE, 0x24, 0x2C, 0xF3, 0xB0, 0x8B, 0x93, 0xD1, 0x8D, 0xBD, 0x61, 0xD4, 0xAB, 0x7C, 0x42, 0xE5, 0x81, 0xDB, 0xFD, 0xBF, 0xD2, 0xF5, 0xD8, 0xED, 0xF8, 0x2C, 0x3B, 0x35, 0xD6 },
74         { 0xE9, 0x7C, 0x79, 0x0B, 0x19, 0x45, 0x32, 0xA5, 0x9B, 0xC8, 0x40, 0x90, 0xB5, 0xC6, 0x8C, 0x5B, 0x0D, 0x05, 0x0C, 0x6F, 0xE9, 0x37, 0xAB, 0xDF, 0x48, 0x0C, 0xC1, 0x9C, 0x34, 0x5B, 0x72, 0xFE, 0xF9, 0x25, 0xD8, 0x3B, 0xF9, 0xB4, 0x2D, 0x1A, 0x8F, 0x57, 0x2A, 0xDE, 0x7A, 0x50, 0x9F, 0xF9 },
75         { 0xA7, 0x72, 0xD7, 0xB9, 0x84, 0xAB, 0xC7, 0x90, 0xA9, 0xFF, 0xF5, 0x1F, 0x3B, 0xD7, 0xC6, 0xA5, 0x38, 0x44, 0xA2, 0x33, 0xA5, 0x64, 0xA9, 0x70, 0x87, 0x2C, 0x41, 0x34, 0x5A, 0xFE, 0x19, 0x98, 0x3B, 0x8D, 0x3C, 0xE3, 0x0B, 0x90, 0x0F, 0xD7, 0xFD, 0xD6, 0x6C, 0xED, 0x03, 0xD0, 0xCD, 0x6E },
76         { 0x61, 0x4A, 0x6B, 0x25, 0xBD, 0x67, 0x32, 0x16, 0xED, 0xEA, 0xB6, 0xA0, 0x51, 0xA8, 0xB4, 0x86, 0x9F, 0x9A, 0xD8, 0x0C, 0xC5, 0xDD, 0x4A, 0xE6, 0x29, 0xDD, 0xFB, 0x70, 0xCA, 0xA7, 0x0E, 0x49, 0xD5, 0x1E, 0x70, 0x27, 0xFF, 0x35, 0xA1, 0x83, 0xA2, 0x78, 0xFE, 0x97, 0xF8, 0x75, 0x9C, 0xF9 }
77 };
78
79 /*
80  * Hash function helper class for std::unordered_set
81  */
82 struct KeyHasher
83 {
84         std::size_t operator()(const std::array<std::uint8_t,MHASH384_SIZE> &key) const
85         {
86                 if(key.size() >= sizeof(std::size_t))
87                 {
88                         return *reinterpret_cast<const size_t*>(key.data());
89                 }
90                 else if(key.size() >= sizeof(uint32_t))
91                 {
92                         return *reinterpret_cast<const uint32_t*>(key.data());
93                 }
94                 else if(key.size() >= sizeof(uint16_t))
95                 {
96                         return *reinterpret_cast<const uint16_t*>(key.data());
97                 }
98                 else
99                 {
100                         return (key.size() > 0U) ? key[0U] : 0U;
101                 }
102         }
103 };
104
105 /*
106  * Comparison function helper class for std::unordered_set
107  */
108 struct KeyEqualTo
109 {
110         bool operator()(const std::array<std::uint8_t,MHASH384_SIZE> &a, const std::array<std::uint8_t,MHASH384_SIZE> &b) const
111         {
112                 if (a.size() == b.size())
113                 {
114                         return (memcmp(a.data(), b.data(), a.size()) == 0);
115                 }
116                 return false;
117         }
118 };
119
120 /*
121  * Unordered set of hash values
122  */
123 typedef std::unordered_set<std::array<std::uint8_t,MHASH384_SIZE>, KeyHasher, KeyEqualTo> UnorderedHashSet;
124 typedef UnorderedHashSet::iterator HashSetIter;
125
126 /*
127  * Read the next input line
128  */
129 static bool read_line(FILE *const input, char *const line, const int max_count, bool &flag)
130 {
131         for(;;)
132         {
133                 const bool discard = flag;
134                 flag = true;
135                 if(fgets(line, max_count, input) != NULL)
136                 {
137                         size_t len = strlen(line);
138                         while((len > 0) && (line[len - 1U] == '\n'))
139                         {
140                                 flag = false; /*line not truncated*/
141                                 line[--len] = '\0';
142                         }
143                         if(!discard)
144                         {
145                                 return true;
146                         }
147                 }
148                 else
149                 {
150                         return false;
151                 }
152         }
153 }
154
155 /*
156  * Compute hash and compare against reference
157  */
158 static bool test_string(const uint32_t count, const char *const text, const uint8_t *const expected, const options_t &options)
159 {
160         MHash384 mhash384;
161         for(uint32_t i = 0U; i < count; ++i)
162         {
163                 mhash384.update(text);
164         }
165
166         const uint8_t *const digest = mhash384.finish();
167         const bool success = (!memcmp(digest, expected, MHASH384_SIZE));
168
169         const std::string string = options.base64 ? bytes_to_base64(digest, MHASH384_SIZE) : bytes_to_hex(digest, MHASH384_SIZE, options.lower_case);
170         const CHAR_T *const result = success ? STR("OK") : STR("Error!");
171         FPRINTF(stderr, STR("%") PRI_char STR(" - %") PRI_CHAR STR("\n"), string.c_str(), result);
172         
173         fflush(stderr);
174         return success;
175 }
176
177 /*
178  * Compute hash and append to hashset
179  */
180 static bool append_string(UnorderedHashSet &hash_set, std::vector<std::array<uint64_t,256U>> &stats, const char *const text, const bool base64, const bool lower_case)
181 {
182         std::array<uint8_t, MHASH384_SIZE> digest;
183         mhash384_compute(digest.data(), reinterpret_cast<const uint8_t*>(text), strlen(text));
184
185         const std::string string = base64 ? bytes_to_base64(digest.data(), MHASH384_SIZE) : bytes_to_hex(digest.data(), MHASH384_SIZE, lower_case);
186         FPRINTF(stderr, STR("%") PRI_char STR("\n"), string.c_str());
187         
188         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
189         {
190                 stats[i][digest[i]]++;
191         }
192
193         std::pair<HashSetIter, bool> result = hash_set.insert(digest);
194         if(!result.second)
195         {
196                 FPRINTF(stderr, STR("Collision detected: \"%") PRI_char STR("\"\n"), text);
197         }
198         
199         fflush(stderr);
200         return result.second;
201 }
202
203 /*
204  * Self-testing routine
205  */
206 bool self_test(const options_t &options)
207 {
208         bool success = mhash384_selftest();
209
210         if(success)
211         {
212                 for(size_t i = 0U; SELFTEST_INPUT[i].count > 0U; ++i)
213                 {
214                         if(!test_string(SELFTEST_INPUT[i].count, SELFTEST_INPUT[i].string, SELFTEST_EXPECTED[i], options))
215                         {
216                                 success = false;
217                                 if(!options.keep_going)
218                                 {
219                                         break; /*failure*/
220                                 }
221                         }
222                 }
223         }
224
225         if(success)
226         {
227                 FPUTS(STR("\nSelf-test completed successfully :-)\n"), stderr);
228         }
229         else
230         {
231                 FPUTS(STR("\nError: Self-test has failed! :-(\n"), stderr);
232         }
233
234         return success;
235 }
236
237 /*
238  * Stress-testing routine
239  */
240 bool stress_test(const CHAR_T *const file_name, const options_t &options)
241 {
242         errno = 0;
243         FILE *const input = file_name ? FOPEN(file_name, STR("r")) : stdin;
244         if(!input)
245         {
246                 FPUTS(STR("Error: Failed to open specified input file for reading!\n"), stderr);
247                 return false;
248         }
249
250         std::vector<std::array<uint64_t,256U>> stats(MHASH384_SIZE);
251         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
252         {
253                 stats[i].fill(0U);
254         }
255
256         UnorderedHashSet hash_set;
257         char line[1024U];
258         bool success = true, flag = false;
259
260         while(read_line(input, line, 1024U, flag))
261         {
262                 /*FPRINTF(stderr, STR("\"%") PRI_char STR("\"\n"), line);*/
263                 if(line[0U])
264                 {
265                         if(!append_string(hash_set, stats, line, options.base64, options.lower_case))
266                         {
267                                 success = false;
268                                 if(!options.keep_going)
269                                 {
270                                         break; /*collison*/
271                                 }
272                         }
273                 }
274         }
275
276         FPUTS(STR("\n[STATS]\n"), stderr);
277         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
278         {
279                 uint64_t min_value = UINT64_MAX;
280                 uint64_t max_value = 0U;
281                 for(size_t j = 0U; j < 256U; ++j)
282                 {
283                         min_value = std::min(min_value, stats[i][j]);
284                         max_value = std::max(max_value, stats[i][j]);
285                 }
286                 const double ratio = (max_value > 0U) ? min_value / static_cast<double>(max_value): 0.0;
287                 FPRINTF(stderr, STR("%02u: %06") STR(PRIu64) STR(" <-> %06") STR(PRIu64) STR(" [%.2f]"), static_cast<unsigned int>(i), min_value, max_value, ratio);
288                 FPUTS(((i % 3U) == 2U) ? STR("\n") : STR("  "), stderr);
289         }
290
291         if(success)
292         {
293                 if(!ferror(input))
294                 {
295                         FPRINTF(stderr, STR("\nStress-test completed successfully. [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
296                 }
297                 else
298                 {
299                         FPUTS(STR("\nError: File read error has occurred!\n"), stderr);
300                         success = false;
301                 }
302         }
303         else
304         {
305                 FPRINTF(stderr, STR("\nStress-test ended *with* collision! [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
306         }
307         
308         if(file_name)
309         {
310                 fclose(input);
311         }
312
313         return success;
314 }