OSDN Git Service

Fixed compilation on FreeBSD (TrueOS) as well as OpenBSD and OpenSolaris (OpenIndiana).
[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 #include <errno.h>
30
31 /*
32  * Test-case specification
33  */
34 typedef struct _test_case_t
35 {
36         uint32_t count;
37         const char *string;
38 }
39 test_case_t;
40
41 /*
42  * Pre-defined test-cases
43  */
44 static const test_case_t SELFTEST_INPUT[] =
45 {
46         { 0x00000001, "" },
47         { 0x00000001, "abc" },
48         { 0x00000001, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" },
49         { 0x00000001, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" },
50         { 0x000186A0, "aaaaaaaaaa" },
51         { 0x01000000, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" },
52         { 0x00000001, "The quick brown fox jumps over the lazy dog" },
53         { 0x00000001, "The quick brown fox jumps over the lazy cog" },
54         { 0x00000001, "Franz jagt im komplett verwahrlosten Taxi quer durch Bayern" },
55         { 0x00000001, "Frank jagt im komplett verwahrlosten Taxi quer durch Bayern" },
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 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." },
57         { 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." },
58         { 0x00000000, NULL} /*EOL*/
59 };
60
61 /*
62  * Expected hash values
63  */
64 static const uint8_t SELFTEST_EXPECTED[][MHASH384_SIZE] =
65 {
66         { 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 },
67         { 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 },
68         { 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 },
69         { 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 },
70         { 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 },
71         { 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 },
72         { 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 },
73         { 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 },
74         { 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 },
75         { 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 },
76         { 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 },
77         { 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 }
78 };
79
80 /*
81  * Hash function helper class for std::unordered_set
82  */
83 struct KeyHasher
84 {
85         std::size_t operator()(const std::array<std::uint8_t,MHASH384_SIZE> &key) const
86         {
87                 if(key.size() >= sizeof(std::size_t))
88                 {
89                         return *reinterpret_cast<const size_t*>(key.data());
90                 }
91                 else if(key.size() >= sizeof(uint32_t))
92                 {
93                         return *reinterpret_cast<const uint32_t*>(key.data());
94                 }
95                 else if(key.size() >= sizeof(uint16_t))
96                 {
97                         return *reinterpret_cast<const uint16_t*>(key.data());
98                 }
99                 else
100                 {
101                         return (key.size() > 0U) ? key[0U] : 0U;
102                 }
103         }
104 };
105
106 /*
107  * Comparison function helper class for std::unordered_set
108  */
109 struct KeyEqualTo
110 {
111         bool operator()(const std::array<std::uint8_t,MHASH384_SIZE> &a, const std::array<std::uint8_t,MHASH384_SIZE> &b) const
112         {
113                 if (a.size() == b.size())
114                 {
115                         return (memcmp(a.data(), b.data(), a.size()) == 0);
116                 }
117                 return false;
118         }
119 };
120
121 /*
122  * Unordered set of hash values
123  */
124 typedef std::unordered_set<std::array<std::uint8_t,MHASH384_SIZE>, KeyHasher, KeyEqualTo> UnorderedHashSet;
125 typedef UnorderedHashSet::iterator HashSetIter;
126
127 /*
128  * Read the next input line
129  */
130 static bool read_line(FILE *const input, char *const line, const int max_count, bool &flag)
131 {
132         for(;;)
133         {
134                 const bool discard = flag;
135                 flag = true;
136                 if(fgets(line, max_count, input) != NULL)
137                 {
138                         size_t len = strlen(line);
139                         while((len > 0) && (line[len - 1U] == '\n'))
140                         {
141                                 flag = false; /*line not truncated*/
142                                 line[--len] = '\0';
143                         }
144                         if(!discard)
145                         {
146                                 return true;
147                         }
148                 }
149                 else
150                 {
151                         return false;
152                 }
153         }
154 }
155
156 /*
157  * Compute hash and compare against reference
158  */
159 static bool test_string(const uint32_t count, const char *const text, const uint8_t *const expected, const options_t &options)
160 {
161         MHash384 mhash384;
162         for(uint32_t i = 0U; i < count; ++i)
163         {
164                 mhash384.update(text);
165         }
166
167         const uint8_t *const digest = mhash384.finish();
168         const bool success = (!memcmp(digest, expected, MHASH384_SIZE));
169
170         const std::string string = options.base64 ? bytes_to_base64(digest, MHASH384_SIZE) : bytes_to_hex(digest, MHASH384_SIZE, options.lower_case);
171         const CHAR_T *const result = success ? STR("OK") : STR("Error!");
172         FPRINTF(stderr, STR("%") PRI_char STR(" - %") PRI_CHAR STR("\n"), string.c_str(), result);
173         
174         fflush(stderr);
175         return success;
176 }
177
178 /*
179  * Compute hash and append to hashset
180  */
181 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)
182 {
183         std::array<uint8_t, MHASH384_SIZE> digest;
184         mhash384_compute(digest.data(), reinterpret_cast<const uint8_t*>(text), strlen(text));
185
186         const std::string string = base64 ? bytes_to_base64(digest.data(), MHASH384_SIZE) : bytes_to_hex(digest.data(), MHASH384_SIZE, lower_case);
187         FPRINTF(stderr, STR("%") PRI_char STR("\n"), string.c_str());
188         
189         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
190         {
191                 stats[i][digest[i]]++;
192         }
193
194         std::pair<HashSetIter, bool> result = hash_set.insert(digest);
195         if(!result.second)
196         {
197                 FPRINTF(stderr, STR("Collision detected: \"%") PRI_char STR("\"\n"), text);
198         }
199         
200         fflush(stderr);
201         return result.second;
202 }
203
204 /*
205  * Self-testing routine
206  */
207 bool self_test(const options_t &options)
208 {
209         bool success = mhash384_selftest();
210
211         if(success)
212         {
213                 for(size_t i = 0U; SELFTEST_INPUT[i].count > 0U; ++i)
214                 {
215                         if(!test_string(SELFTEST_INPUT[i].count, SELFTEST_INPUT[i].string, SELFTEST_EXPECTED[i], options))
216                         {
217                                 success = false;
218                                 if(!options.keep_going)
219                                 {
220                                         break; /*failure*/
221                                 }
222                         }
223                 }
224         }
225
226         if(success)
227         {
228                 FPUTS(STR("\nSelf-test completed successfully :-)\n"), stderr);
229         }
230         else
231         {
232                 FPUTS(STR("\nError: Self-test has failed! :-(\n"), stderr);
233         }
234
235         return success;
236 }
237
238 /*
239  * Stress-testing routine
240  */
241 bool stress_test(const CHAR_T *const file_name, const options_t &options)
242 {
243         errno = 0;
244         FILE *const input = file_name ? FOPEN(file_name, STR("r")) : stdin;
245         if(!input)
246         {
247                 FPUTS(STR("Error: Failed to open specified input file for reading!\n"), stderr);
248                 return false;
249         }
250
251         std::vector<std::array<uint64_t,256U>> stats(MHASH384_SIZE);
252         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
253         {
254                 stats[i].fill(0U);
255         }
256
257         UnorderedHashSet hash_set;
258         char line[1024U];
259         bool success = true, flag = false;
260
261         while(read_line(input, line, 1024U, flag))
262         {
263                 /*FPRINTF(stderr, STR("\"%") PRI_char STR("\"\n"), line);*/
264                 if(line[0U])
265                 {
266                         if(!append_string(hash_set, stats, line, options.base64, options.lower_case))
267                         {
268                                 success = false;
269                                 if(!options.keep_going)
270                                 {
271                                         break; /*collison*/
272                                 }
273                         }
274                 }
275         }
276
277         FPUTS(STR("\n[STATS]\n"), stderr);
278         for(size_t i = 0U; i < MHASH384_SIZE; ++i)
279         {
280                 uint64_t min_value = UINT64_MAX;
281                 uint64_t max_value = 0U;
282                 for(size_t j = 0U; j < 256U; ++j)
283                 {
284                         min_value = std::min(min_value, stats[i][j]);
285                         max_value = std::max(max_value, stats[i][j]);
286                 }
287                 const double ratio = (max_value > 0U) ? min_value / static_cast<double>(max_value): 0.0;
288                 FPRINTF(stderr, STR("%02u: %06") STR(PRIu64) STR(" <-> %06") STR(PRIu64) STR(" [%.2f]"), static_cast<unsigned int>(i), min_value, max_value, ratio);
289                 FPUTS(((i % 3U) == 2U) ? STR("\n") : STR("  "), stderr);
290         }
291
292         if(success)
293         {
294                 if(!ferror(input))
295                 {
296                         FPRINTF(stderr, STR("\nStress-test completed successfully. [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
297                 }
298                 else
299                 {
300                         FPUTS(STR("\nError: File read error has occurred!\n"), stderr);
301                         success = false;
302                 }
303         }
304         else
305         {
306                 FPRINTF(stderr, STR("\nStress-test ended *with* collision! [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
307         }
308         
309         if(file_name)
310         {
311                 fclose(input);
312         }
313
314         return success;
315 }