• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 29801022385

21 Jul 2026 02:12AM UTC coverage: 89.404% (-0.02%) from 89.419%
29801022385

push

github

web-flow
Merge pull request #5747 from randombit/jack/zfec-avx512-gfni

ZFEC optimizations and improvements

114453 of 128018 relevant lines covered (89.4%)

10848451.21 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.4
/src/tests/test_hash.cpp
1
/*
2
* (C) 2014,2015,2018,2019 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "tests.h"
8

9
#if defined(BOTAN_HAS_HASH)
10
   #include <botan/exceptn.h>
11
   #include <botan/hash.h>
12
   #include <botan/rng.h>
13
   #include <botan/internal/fmt.h>
14
#endif
15

16
namespace Botan_Tests {
17

18
#if defined(BOTAN_HAS_HASH)
19

20
namespace {
21

22
class Invalid_Hash_Name_Tests final : public Test {
1✔
23
   public:
24
      std::vector<Test::Result> run() override {
1✔
25
         Test::Result result("Invalid HashFunction names");
1✔
26
         test_invalid_name(result, "NonExistentHash");
2✔
27
         test_invalid_name(result, "Blake2b(9)", "Bad output bits size for BLAKE2b");
2✔
28
         test_invalid_name(result, "Comb4P(MD5,MD5)", "Comb4P: Must use two distinct hashes");
2✔
29
         test_invalid_name(result, "Comb4P(MD5,SHA-256)", "Comb4P: Incompatible hashes MD5 and SHA-256");
2✔
30
         test_invalid_name(result, "Keccak-1600(160)", "Keccak_1600: Invalid output length 160");
2✔
31
         test_invalid_name(result, "SHA-3(160)", "SHA_3: Invalid output length 160");
2✔
32

33
         return {result};
3✔
34
      }
2✔
35

36
   private:
37
      static void test_invalid_name(Result& result, const std::string& name, const std::string& expected_msg = "") {
6✔
38
         try {
6✔
39
            auto hash = Botan::HashFunction::create_or_throw(name);
6✔
40
            result.test_failure("Was successfully able to create " + name);
×
41
         } catch(Botan::Invalid_Argument& e) {
6✔
42
            const std::string msg = e.what();
5✔
43
            const std::string full_msg = "" + expected_msg;
5✔
44
            result.test_str_eq("expected error message", msg, full_msg);
5✔
45
         } catch(Botan::Lookup_Error& e) {
6✔
46
            const std::string algo_not_found_msg = "Unavailable Hash " + name;
1✔
47
            const std::string msg = e.what();
1✔
48
            result.test_str_eq("expected error message", msg, algo_not_found_msg);
1✔
49
         } catch(std::exception& e) {
1✔
50
            result.test_failure("some unknown exception", e.what());
×
51
         } catch(...) {
×
52
            result.test_failure("some unknown exception");
×
53
         }
×
54
      }
6✔
55
};
56

57
BOTAN_REGISTER_TEST("hash", "invalid_name_hash", Invalid_Hash_Name_Tests);
58

59
class Hash_Function_Tests final : public Text_Based_Test {
×
60
   public:
61
      Hash_Function_Tests() : Text_Based_Test("hash", "In,Out") {}
2✔
62

63
      std::vector<std::string> possible_providers(const std::string& algo) override {
7,593✔
64
         return provider_filter(Botan::HashFunction::providers(algo));
7,593✔
65
      }
66

67
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
7,593✔
68
         const std::vector<uint8_t> input = vars.get_req_bin("In");
7,593✔
69
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
7,593✔
70

71
         Test::Result result(algo);
7,593✔
72

73
         const std::vector<std::string> providers = possible_providers(algo);
7,593✔
74

75
         if(providers.empty()) {
7,593✔
76
            result.note_missing("hash " + algo);
×
77
            return result;
×
78
         }
79

80
         for(const auto& provider_ask : providers) {
15,186✔
81
            auto hash = Botan::HashFunction::create(algo, provider_ask);
7,593✔
82

83
            if(!hash) {
7,593✔
84
               result.test_failure(Botan::fmt("Hash {} supported by {} but not found", algo, provider_ask));
×
85
               continue;
×
86
            }
87

88
            auto clone = hash->new_object();
7,593✔
89

90
            const std::string provider(hash->provider());
7,593✔
91
            result.test_str_not_empty("provider", provider);
7,593✔
92
            result.test_str_eq(provider, hash->name(), algo);
7,593✔
93
            result.test_str_eq(provider, hash->name(), clone->name());
15,186✔
94

95
            // A security level beyond the generic birthday attack is nonsensical
96
            result.test_sz_lte(provider + " security level is bounded by the birthday attack",
15,186✔
97
                               hash->security_level(),
7,593✔
98
                               4 * hash->output_length());
7,593✔
99

100
            for(size_t i = 0; i != 3; ++i) {
30,372✔
101
               hash->update(input);
22,779✔
102
               result.test_bin_eq(provider + " hashing", hash->final(), expected);
91,116✔
103
            }
104

105
            clone->update(input);
7,593✔
106
            result.test_bin_eq(provider + " hashing (clone)", clone->final(), expected);
22,779✔
107

108
            // Test to make sure clear() resets what we need it to
109
            hash->update("some discarded input");
7,593✔
110
            hash->clear();
7,593✔
111
            hash->update(nullptr, 0);  // this should be effectively ignored
7,593✔
112
            hash->update(input);
7,593✔
113

114
            result.test_bin_eq(provider + " hashing after clear", hash->final(), expected);
22,779✔
115

116
            // Test that misaligned inputs work
117

118
            if(!input.empty()) {
7,593✔
119
               std::vector<uint8_t> misaligned = input;
7,533✔
120
               const size_t current_alignment = reinterpret_cast<uintptr_t>(misaligned.data()) % 16;
7,533✔
121

122
               const size_t bytes_to_misalign = 15 - current_alignment;
7,533✔
123

124
               for(size_t i = 0; i != bytes_to_misalign; ++i) {
120,528✔
125
                  misaligned.insert(misaligned.begin(), 0x23);
112,995✔
126
               }
127

128
               hash->update(&misaligned[bytes_to_misalign], input.size());
7,533✔
129
               result.test_bin_eq(provider + " hashing misaligned data", hash->final(), expected);
30,132✔
130
            }
7,533✔
131

132
            if(input.size() > 5) {
7,593✔
133
               hash->update(input[0]);
7,268✔
134

135
               auto fork = hash->copy_state();
7,268✔
136
               // verify fork copy doesn't affect original computation
137
               fork->update(&input[1], input.size() - 2);
7,268✔
138

139
               size_t so_far = 1;
7,268✔
140
               while(so_far < input.size()) {
59,721✔
141
                  size_t take = this->rng().next_byte() % (input.size() - so_far);
52,453✔
142

143
                  if(input.size() - so_far == 1) {
52,453✔
144
                     take = 1;
7,268✔
145
                  }
146

147
                  hash->update(&input[so_far], take);
52,453✔
148
                  so_far += take;
52,453✔
149
               }
150
               result.test_bin_eq(provider + " hashing split", hash->final(), expected);
21,804✔
151

152
               fork->update(&input[input.size() - 1], 1);
7,268✔
153
               result.test_bin_eq(provider + " hashing split", fork->final(), expected);
29,072✔
154
            }
7,268✔
155

156
            if(hash->hash_block_size() > 0) {
7,593✔
157
               // GOST-34.11 uses 32 byte block
158
               result.test_sz_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32);
7,381✔
159
            }
160
         }
22,779✔
161

162
         return result;
163
      }
90,406✔
164
};
165

166
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("hash", "hash_algos", Hash_Function_Tests);
167

168
class Hash_NIST_MonteCarlo_Tests final : public Text_Based_Test {
×
169
   public:
170
      Hash_NIST_MonteCarlo_Tests() : Text_Based_Test("hash_mc.vec", "Seed,Count,Output") {}
2✔
171

172
      std::vector<std::string> possible_providers(const std::string& algo) override {
7✔
173
         return provider_filter(Botan::HashFunction::providers(algo));
7✔
174
      }
175

176
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
7✔
177
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
7✔
178
         const size_t count = vars.get_req_sz("Count");
7✔
179
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
7✔
180

181
         Test::Result result("NIST Monte Carlo " + algo);
7✔
182

183
         const std::vector<std::string> providers = possible_providers(algo);
7✔
184

185
         if(providers.empty()) {
7✔
186
            result.note_missing("hash " + algo);
1✔
187
            return result;
1✔
188
         }
189

190
         for(const auto& provider_ask : providers) {
12✔
191
            auto hash = Botan::HashFunction::create(algo, provider_ask);
6✔
192

193
            if(!hash) {
6✔
194
               result.test_failure(Botan::fmt("Hash {} supported by {} but not found", algo, provider_ask));
×
195
               continue;
×
196
            }
197

198
            std::vector<std::vector<uint8_t>> input;
6✔
199
            input.push_back(seed);
6✔
200
            input.push_back(seed);
6✔
201
            input.push_back(seed);
6✔
202

203
            for(size_t j = 0; j <= count; ++j) {
606✔
204
               for(size_t i = 3; i != 1003; ++i) {
600,600✔
205
                  hash->update(input[0]);
600,000✔
206
                  hash->update(input[1]);
600,000✔
207
                  hash->update(input[2]);
600,000✔
208

209
                  hash->final(input[0].data());
600,000✔
210
                  input[0].swap(input[1]);
600,000✔
211
                  input[1].swap(input[2]);
600,000✔
212
               }
213

214
               if(j < count) {
600✔
215
                  input[0] = input[2];
594✔
216
                  input[1] = input[2];
594✔
217
               }
218
            }
219

220
            result.test_bin_eq("Output is expected", input[2], expected);
6✔
221
         }
12✔
222

223
         return result;
224
      }
21✔
225
};
226

227
BOTAN_REGISTER_TEST("hash", "hash_nist_mc", Hash_NIST_MonteCarlo_Tests);
228

229
class Hash_LongRepeat_Tests final : public Text_Based_Test {
×
230
   public:
231
      Hash_LongRepeat_Tests() : Text_Based_Test("hash_rep.vec", "Input,TotalLength,Digest") {}
2✔
232

233
      std::vector<std::string> possible_providers(const std::string& algo) override {
18✔
234
         return provider_filter(Botan::HashFunction::providers(algo));
18✔
235
      }
236

237
      // repeating the output several times reduces buffering overhead during processing
238
      static std::vector<uint8_t> expand_input(const std::vector<uint8_t>& input, size_t min_len) {
18✔
239
         std::vector<uint8_t> output;
18✔
240
         output.reserve(min_len);
18✔
241

242
         while(output.size() < min_len) {
2,358✔
243
            output.insert(output.end(), input.begin(), input.end());
2,340✔
244
         }
245

246
         return output;
18✔
247
      }
×
248

249
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
18✔
250
         const std::vector<uint8_t> input = expand_input(vars.get_req_bin("Input"), 256);
18✔
251
         const size_t total_len = vars.get_req_sz("TotalLength");
18✔
252
         const std::vector<uint8_t> expected = vars.get_req_bin("Digest");
18✔
253

254
         Test::Result result("Long input " + algo);
18✔
255

256
         const std::vector<std::string> providers = possible_providers(algo);
18✔
257

258
         if(total_len > 1000000 && Test::run_long_tests() == false) {
18✔
259
            return result;
260
         }
261

262
         if(providers.empty()) {
18✔
263
            result.note_missing("hash " + algo);
×
264
            return result;
×
265
         }
266

267
         for(const auto& provider_ask : providers) {
36✔
268
            auto hash = Botan::HashFunction::create(algo, provider_ask);
18✔
269

270
            if(!hash) {
18✔
271
               result.test_failure(Botan::fmt("Hash {} supported by {} but not found", algo, provider_ask));
×
272
               continue;
×
273
            }
274

275
            const size_t full_inputs = total_len / input.size();
18✔
276
            const size_t leftover = total_len % input.size();
18✔
277

278
            for(size_t i = 0; i != full_inputs; ++i) {
37,783,908✔
279
               hash->update(input);
37,783,890✔
280
            }
281

282
            if(leftover > 0) {
18✔
283
               hash->update(input.data(), leftover);
9✔
284
            }
285

286
            std::vector<uint8_t> output(hash->output_length());
18✔
287
            hash->final(output.data());
18✔
288
            result.test_bin_eq("Output is expected", output, expected);
18✔
289
         }
36✔
290

291
         return result;
292
      }
54✔
293
};
294

295
BOTAN_REGISTER_TEST("hash", "hash_rep", Hash_LongRepeat_Tests);
296

297
Test::Result hash_security_level_tests() {
1✔
298
   Test::Result result("HashFunction security level");
1✔
299

300
   const std::vector<std::pair<std::string, size_t>> expected_levels = {
1✔
301
      {"Adler32", 0},
1✔
302
      {"CRC24", 0},
1✔
303
      {"CRC32", 0},
1✔
304
      {"MD4", 0},
1✔
305
      {"MD5", 0},
1✔
306
      {"SHA-1", 61},
1✔
307
      {"SHA-224", 112},
1✔
308
      {"SHA-256", 128},
1✔
309
      {"SHA-384", 192},
1✔
310
      {"SHA-512", 256},
1✔
311
      {"SHA-512-256", 128},
1✔
312
      {"SHA-3(224)", 112},
1✔
313
      {"SHA-3(256)", 128},
1✔
314
      {"SHA-3(384)", 192},
1✔
315
      {"SHA-3(512)", 256},
1✔
316
      {"Keccak-1600(512)", 256},
1✔
317
      {"SHAKE-128(128)", 64},
1✔
318
      {"SHAKE-128(256)", 128},
1✔
319
      {"SHAKE-128(1024)", 128},
1✔
320
      {"SHAKE-256(256)", 128},
1✔
321
      {"SHAKE-256(512)", 256},
1✔
322
      {"SHAKE-256(2048)", 256},
1✔
323
      {"Ascon-Hash256", 128},
1✔
324
      {"Blake2b(512)", 256},
1✔
325
      {"Blake2s(256)", 128},
1✔
326
      {"GOST-R-34.11-94", 105},
1✔
327
      {"RIPEMD-160", 80},
1✔
328
      {"SM3", 128},
1✔
329
      {"Skein-512(512)", 256},
1✔
330
      {"Streebog-256", 128},
1✔
331
      {"Streebog-512", 256},
1✔
332
      {"Whirlpool", 256},
1✔
333
      {"Truncated(SHA-256,64)", 32},
1✔
334
      {"Truncated(SHA-512,300)", 150},
1✔
335
      {"Parallel(MD5,SHA-256)", 128},
1✔
336
      {"Comb4P(MD4,MD5)", 0},
1✔
337
   };
37✔
338

339
   for(const auto& [name, expected] : expected_levels) {
37✔
340
      if(auto hash = Botan::HashFunction::create(name)) {
36✔
341
         result.test_sz_eq(name + " security level", hash->security_level(), expected);
72✔
342
      }
36✔
343
   }
344

345
   return result;
1✔
346
}
2✔
347

348
BOTAN_REGISTER_TEST_FN("hash", "hash_security_level", hash_security_level_tests);
349

350
   #if defined(BOTAN_HAS_TRUNCATED_HASH) && defined(BOTAN_HAS_SHA2_32)
351

352
/// negative tests for Truncated_Hash, positive tests are implemented in hash/truncated.vec
353
Test::Result hash_truncation_negative_tests() {
1✔
354
   Test::Result result("hash truncation parameter validation");
1✔
355
   result.test_throws<Botan::Invalid_Argument>("truncation to zero",
1✔
356
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,0)"); });
1✔
357
   result.test_throws<Botan::Invalid_Argument>("cannot output more bits than the underlying hash",
1✔
358
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,257)"); });
1✔
359
   auto unobtainable = Botan::HashFunction::create("Truncated(NonExistentHash-256,128)");
1✔
360
   result.test_is_true("non-existent hashes are not created", unobtainable == nullptr);
1✔
361
   return result;
1✔
362
}
1✔
363

364
BOTAN_REGISTER_TEST_FN("hash", "hash_truncation", hash_truncation_negative_tests);
365

366
   #endif
367

368
}  // namespace
369

370
#endif
371

372
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc