1 /* ---------------------------------------------------------------------------------------------- */
2 /* MHash-384 - Simple fast portable secure hashing library */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de> */
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: */
11 /* The above copyright notice and this permission notice shall be included in all copies or */
12 /* substantial portions of the Software. */
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 /* ---------------------------------------------------------------------------------------------- */
21 #include "self_test.h"
26 #include <unordered_set>
33 * Test-case specification
35 typedef struct _test_case_t
43 * Pre-defined test-cases
45 static const test_case_t SELFTEST_INPUT[] =
48 { 0x00000001, "abc" },
49 { 0x00000001, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" },
50 { 0x00000001, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" },
51 { 0x000186A0, "aaaaaaaaaa" },
52 { 0x01000000, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno" },
53 { 0x00000001, "The quick brown fox jumps over the lazy dog" },
54 { 0x00000001, "The quick brown fox jumps over the lazy cog" },
55 { 0x00000001, "Franz jagt im komplett verwahrlosten Taxi quer durch Bayern" },
56 { 0x00000001, "Frank jagt im komplett verwahrlosten Taxi quer durch Bayern" },
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 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." },
58 { 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." },
59 { 0x00000000, NULL} /*EOL*/
63 * Expected hash values
65 static const uint8_t SELFTEST_EXPECTED[][MHASH384_SIZE] =
67 { 0xD6, 0x29, 0xCB, 0x37, 0xF7, 0x61, 0x56, 0x62, 0x1E, 0x09, 0xC9, 0x3B, 0x8D, 0x10, 0x29, 0xC5, 0xF0, 0xF7, 0xB7, 0x78, 0xDE, 0x5B, 0xD9, 0x57, 0x82, 0x30, 0x67, 0x3F, 0xE7, 0xFA, 0x4C, 0x45, 0xA2, 0x30, 0x8A, 0xB4, 0xE9, 0xD9, 0x9A, 0xA0, 0xEA, 0x19, 0x4C, 0xBF, 0x62, 0x30, 0xC2, 0x02 },
68 { 0x1D, 0x7B, 0x15, 0x93, 0xD7, 0xAA, 0x4D, 0xD7, 0xDC, 0x8D, 0xA6, 0xD6, 0xBD, 0x59, 0x1D, 0x8B, 0x50, 0x7C, 0xA5, 0x14, 0xE1, 0xC7, 0xEC, 0x37, 0xF4, 0x3E, 0x78, 0xDD, 0x6B, 0x83, 0x25, 0x3C, 0x35, 0x54, 0x03, 0xF6, 0x10, 0x09, 0x58, 0x28, 0x90, 0x48, 0x0C, 0x4C, 0xD5, 0x27, 0xF4, 0x05 },
69 { 0x9B, 0x98, 0xCE, 0x4C, 0x3F, 0x4E, 0xA9, 0x01, 0x8E, 0x1E, 0xD3, 0x03, 0x47, 0x17, 0xE3, 0xE5, 0xD8, 0xDA, 0x20, 0xAB, 0x37, 0x5B, 0xAF, 0xE3, 0x72, 0xD0, 0x77, 0x80, 0xFF, 0xDE, 0xFD, 0xA3, 0x2B, 0xA3, 0xC8, 0x22, 0x15, 0x5E, 0x28, 0x83, 0xE3, 0x33, 0xAF, 0xCA, 0xAE, 0xAF, 0xA6, 0x14 },
70 { 0x45, 0x41, 0x6D, 0x1F, 0xFD, 0x44, 0xD3, 0x94, 0x0C, 0x5A, 0xF5, 0xB4, 0x25, 0xB5, 0xDA, 0x07, 0xFD, 0x8C, 0x06, 0xBA, 0xB7, 0xC5, 0x8C, 0xD1, 0x33, 0x89, 0xBC, 0x28, 0x46, 0x50, 0xED, 0x60, 0x90, 0xA9, 0xA1, 0x95, 0x2C, 0x29, 0xFE, 0xB1, 0xB2, 0xC6, 0xAF, 0xBF, 0x5F, 0x99, 0x61, 0x08 },
71 { 0x20, 0x77, 0x4C, 0x57, 0x0A, 0xE2, 0xE6, 0xA4, 0x9A, 0x01, 0xDA, 0xE9, 0x48, 0x67, 0x2A, 0xE9, 0x4D, 0x46, 0x5D, 0x91, 0x67, 0x13, 0x0F, 0xE8, 0x7B, 0xF2, 0x41, 0x48, 0x64, 0x9C, 0xF1, 0x21, 0xE8, 0xA5, 0x07, 0x59, 0x50, 0x7B, 0xF9, 0x71, 0x34, 0xF0, 0xD2, 0x8C, 0xEE, 0x91, 0xDC, 0xA2 },
72 { 0xC1, 0x88, 0x43, 0xD4, 0xDD, 0x9E, 0x04, 0x79, 0xD3, 0x8A, 0x55, 0xE8, 0xC7, 0x5E, 0x22, 0x01, 0xF0, 0xE9, 0x3B, 0x6E, 0xF1, 0xF9, 0x16, 0xEB, 0xAF, 0x3C, 0x40, 0x74, 0xD1, 0x88, 0x94, 0x5C, 0x7B, 0x96, 0x57, 0xF4, 0x52, 0x69, 0xA1, 0xDA, 0xAB, 0x4A, 0x8F, 0xB6, 0xF6, 0x3D, 0x0B, 0xEE },
73 { 0xE2, 0x90, 0x1E, 0xDB, 0x76, 0xAA, 0x83, 0xF2, 0xD5, 0x2F, 0xEA, 0x9A, 0x34, 0xEB, 0x2B, 0xC0, 0x5E, 0xDF, 0xA9, 0x57, 0x1C, 0x81, 0x26, 0x20, 0x24, 0x5B, 0xBE, 0xBA, 0x95, 0xFA, 0x2D, 0x82, 0xCB, 0xB5, 0xBA, 0xD9, 0x3E, 0xB6, 0x4D, 0x4B, 0x72, 0xB4, 0x66, 0x35, 0xD5, 0x65, 0x40, 0x0B },
74 { 0x8B, 0x81, 0xCD, 0x9C, 0x1D, 0xEE, 0x8B, 0xCC, 0x64, 0x2F, 0x8B, 0x1E, 0xA9, 0x82, 0xCE, 0x79, 0xD1, 0x5F, 0x50, 0xE6, 0x69, 0x39, 0xFF, 0x34, 0x29, 0x4E, 0x63, 0xD9, 0x3A, 0xE3, 0xC6, 0x10, 0xF9, 0x46, 0x4F, 0xEF, 0xEF, 0x30, 0x29, 0x74, 0x09, 0xB6, 0x22, 0x31, 0xF0, 0x28, 0xAB, 0xC0 },
75 { 0xA5, 0x45, 0xE6, 0x31, 0x9F, 0x6A, 0xCE, 0x0C, 0xEF, 0x62, 0xBC, 0xAA, 0xF5, 0xD4, 0x88, 0x85, 0x87, 0x51, 0xB9, 0x6C, 0x2A, 0x48, 0x8D, 0x5B, 0xE6, 0xEC, 0x63, 0xD5, 0x00, 0x06, 0xC9, 0xD5, 0x4D, 0xAD, 0x66, 0x16, 0x84, 0x88, 0xF0, 0x3B, 0x4A, 0x86, 0x8D, 0x3B, 0xA6, 0x10, 0xCC, 0xF8 },
76 { 0x2B, 0xEC, 0xB8, 0x9D, 0xD5, 0x5F, 0xC9, 0xF2, 0xF3, 0x38, 0x4D, 0xF6, 0x1D, 0x62, 0x40, 0xDD, 0xA5, 0x4F, 0xC2, 0x75, 0xD1, 0x86, 0xA4, 0x5A, 0x88, 0x3C, 0x03, 0xAA, 0x87, 0x3D, 0xDA, 0xF4, 0xFA, 0xD4, 0x74, 0x42, 0x12, 0xB9, 0x2D, 0x26, 0x05, 0x4B, 0x92, 0x85, 0x2D, 0xEA, 0x5F, 0x1D },
77 { 0x62, 0x83, 0xCE, 0x91, 0xDA, 0x8A, 0xF5, 0x09, 0x1C, 0x7B, 0x06, 0xE1, 0x35, 0x2D, 0xC9, 0x47, 0xB9, 0xE3, 0xB8, 0x96, 0x14, 0x80, 0x7C, 0x04, 0xC4, 0x3D, 0xA3, 0x22, 0x32, 0x1F, 0xBA, 0xDA, 0xD3, 0xCC, 0xCA, 0xBE, 0xCB, 0x5C, 0x1B, 0x1B, 0x26, 0x54, 0x32, 0xA6, 0x03, 0x91, 0x21, 0xE1 },
78 { 0x5D, 0xCB, 0x7C, 0x4B, 0x27, 0xBA, 0x84, 0x16, 0x88, 0xFB, 0x6A, 0x86, 0x0E, 0x97, 0xA4, 0x52, 0x56, 0x91, 0x75, 0x33, 0xF6, 0x34, 0x48, 0xCF, 0x66, 0x67, 0x81, 0xEA, 0x4B, 0x5F, 0xAB, 0x20, 0xE1, 0xC7, 0x46, 0xC8, 0x82, 0x10, 0x99, 0xA9, 0x15, 0x22, 0x2E, 0x0D, 0x77, 0x62, 0x40, 0x98 }
82 * Hash function helper class for std::unordered_set
86 std::size_t operator()(const std::array<std::uint8_t,MHASH384_SIZE> &key) const
88 if(key.size() >= sizeof(std::size_t))
90 return *reinterpret_cast<const size_t*>(key.data());
92 else if(key.size() >= sizeof(uint32_t))
94 return *reinterpret_cast<const uint32_t*>(key.data());
96 else if(key.size() >= sizeof(uint16_t))
98 return *reinterpret_cast<const uint16_t*>(key.data());
102 return (key.size() > 0U) ? key[0U] : 0U;
108 * Comparison function helper class for std::unordered_set
112 bool operator()(const std::array<std::uint8_t,MHASH384_SIZE> &a, const std::array<std::uint8_t,MHASH384_SIZE> &b) const
114 if (a.size() == b.size())
116 return (memcmp(a.data(), b.data(), a.size()) == 0);
123 * Unordered set of hash values
125 typedef std::unordered_set<std::array<std::uint8_t,MHASH384_SIZE>, KeyHasher, KeyEqualTo> UnorderedHashSet;
126 typedef UnorderedHashSet::iterator HashSetIter;
129 * Compute hash and compare against reference
131 static bool test_string(const uint32_t count, const char *const text, const uint8_t *const expected, const options_t &options)
134 for(uint32_t i = 0U; i < count; ++i)
136 mhash384.update(text);
139 const uint8_t *const digest = mhash384.finish();
140 const bool success = (!memcmp(digest, expected, MHASH384_SIZE));
142 const std::string string = options.base64 ? bytes_to_base64(digest, MHASH384_SIZE) : bytes_to_hex(digest, MHASH384_SIZE, options.lower_case);
143 const CHAR_T *const result = success ? STR("OK") : STR("Error!");
144 FPRINTF(stderr, STR("%") PRI_char STR(" - %") PRI_CHAR STR("\n"), string.c_str(), result);
151 * Compute hash and append to hashset
153 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)
155 std::array<uint8_t, MHASH384_SIZE> digest;
156 mhash384_compute(digest.data(), reinterpret_cast<const uint8_t*>(text), strlen(text));
158 const std::string string = base64 ? bytes_to_base64(digest.data(), MHASH384_SIZE) : bytes_to_hex(digest.data(), MHASH384_SIZE, lower_case);
159 FPRINTF(stderr, STR("%") PRI_char STR("\n"), string.c_str());
161 for(size_t i = 0U; i < MHASH384_SIZE; ++i)
163 stats[i][digest[i]]++;
166 std::pair<HashSetIter, bool> result = hash_set.insert(digest);
169 FPRINTF(stderr, STR("Collision detected: \"%") PRI_char STR("\"\n"), text);
173 return result.second;
177 * Self-testing routine
179 bool self_test(const options_t &options)
181 bool success = mhash384_selftest();
185 for(size_t i = 0U; SELFTEST_INPUT[i].count > 0U; ++i)
187 if(!test_string(SELFTEST_INPUT[i].count, SELFTEST_INPUT[i].string, SELFTEST_EXPECTED[i], options))
190 if(!options.keep_going)
200 FPUTS(STR("\nSelf-test completed successfully :-)\n"), stderr);
204 FPUTS(STR("\nError: Self-test has failed! :-(\n"), stderr);
211 * Stress-testing routine
213 bool stress_test(const CHAR_T *const file_name, const options_t &options)
215 std::ifstream infile;
218 infile.open(file_name);
221 std::istream &instream = file_name ? infile : std::cin;
224 FPUTS(STR("Error: Failed to open input file for reading!\n"), stderr);
228 std::vector<std::array<uint64_t,256U>> stats(MHASH384_SIZE);
229 for(size_t i = 0U; i < MHASH384_SIZE; ++i)
235 UnorderedHashSet hash_set;
238 while(instream.good())
240 std::getline(instream, line);
243 if(!append_string(hash_set, stats, line.c_str(), options.base64, options.lower_case))
246 if(!options.keep_going)
254 FPUTS(STR("\n[STATS]\n"), stderr);
255 for(size_t i = 0U; i < MHASH384_SIZE; ++i)
257 uint64_t min_value = UINT64_MAX;
258 uint64_t max_value = 0U;
259 for(size_t j = 0U; j < 256U; ++j)
261 min_value = std::min(min_value, stats[i][j]);
262 max_value = std::max(max_value, stats[i][j]);
264 const double ratio = (max_value > 0U) ? min_value / static_cast<double>(max_value): 0.0;
265 FPRINTF(stderr, STR("%02u: %06") STR(PRIu64) STR(" <-> %06") STR(PRIu64) STR(" [%.2f]"), static_cast<unsigned int>(i), min_value, max_value, ratio);
266 FPUTS(((i % 3U) == 2U) ? STR("\n") : STR(" "), stderr);
271 if(instream.eof() && (!instream.bad()))
273 FPRINTF(stderr, STR("\nStress-test completed successfully. [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
277 FPUTS(STR("\nError: File read error has occurred!\n"), stderr);
283 FPRINTF(stderr, STR("\nStress-test ended *with* collision! [%") STR(PRIu64) STR("]\n"), static_cast<uint64_t>(hash_set.size()));
286 if(file_name && infile.is_open())