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

randombit / botan / 11844561993

14 Nov 2024 07:58PM UTC coverage: 91.178% (+0.1%) from 91.072%
11844561993

Pull #4435

github

web-flow
Merge 81dcb29da into e430f157a
Pull Request #4435: Test duration values ​​are now presented in seconds with six digits of precision. Tests without time measurements have been edited.

91856 of 100744 relevant lines covered (91.18%)

9311006.71 hits per line

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

89.07
/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/hash.h>
11
   #include <botan/internal/fmt.h>
12
#endif
13

14
namespace Botan_Tests {
15

16
#if defined(BOTAN_HAS_HASH)
17

18
namespace {
19

20
class Invalid_Hash_Name_Tests final : public Test {
×
21
   public:
22
      std::vector<Test::Result> run() override {
1✔
23
         Test::Result result("Invalid HashFunction names");
1✔
24
         result.start_timer();
1✔
25

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
         result.end_timer();
1✔
34
         return {result};
3✔
35
      }
2✔
36

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

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

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

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

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

72
         Test::Result result(algo);
13,798✔
73
         result.start_timer();
6,899✔
74

75
         const std::vector<std::string> providers = possible_providers(algo);
6,899✔
76

77
         if(providers.empty()) {
6,899✔
78
            result.note_missing("hash " + algo);
×
79
            return result;
×
80
         }
81

82
         for(const auto& provider_ask : providers) {
13,798✔
83
            auto hash = Botan::HashFunction::create(algo, provider_ask);
6,899✔
84

85
            if(!hash) {
6,899✔
86
               result.test_failure(Botan::fmt("Hash {} supported by {} but not found", algo, provider_ask));
×
87
               continue;
×
88
            }
89

90
            auto clone = hash->new_object();
6,899✔
91

92
            const std::string provider(hash->provider());
6,899✔
93
            result.test_is_nonempty("provider", provider);
6,899✔
94
            result.test_eq(provider, hash->name(), algo);
6,899✔
95
            result.test_eq(provider, hash->name(), clone->name());
13,798✔
96

97
            for(size_t i = 0; i != 3; ++i) {
27,596✔
98
               hash->update(input);
20,697✔
99
               result.test_eq(provider, "hashing", hash->final(), expected);
62,091✔
100
            }
101

102
            clone->update(input);
6,899✔
103
            result.test_eq(provider, "hashing (clone)", clone->final(), expected);
13,798✔
104

105
            // Test to make sure clear() resets what we need it to
106
            hash->update("some discarded input");
6,899✔
107
            hash->clear();
6,899✔
108
            hash->update(nullptr, 0);  // this should be effectively ignored
6,899✔
109
            hash->update(input);
6,899✔
110

111
            result.test_eq(provider, "hashing after clear", hash->final(), expected);
13,798✔
112

113
            // Test that misaligned inputs work
114

115
            if(!input.empty()) {
6,899✔
116
               std::vector<uint8_t> misaligned = input;
6,846✔
117
               const size_t current_alignment = reinterpret_cast<uintptr_t>(misaligned.data()) % 16;
6,846✔
118

119
               const size_t bytes_to_misalign = 15 - current_alignment;
6,846✔
120

121
               for(size_t i = 0; i != bytes_to_misalign; ++i) {
109,536✔
122
                  misaligned.insert(misaligned.begin(), 0x23);
102,690✔
123
               }
124

125
               hash->update(&misaligned[bytes_to_misalign], input.size());
6,846✔
126
               result.test_eq(provider, "hashing misaligned data", hash->final(), expected);
20,538✔
127
            }
6,846✔
128

129
            if(input.size() > 5) {
6,899✔
130
               hash->update(input[0]);
6,610✔
131

132
               auto fork = hash->copy_state();
6,610✔
133
               // verify fork copy doesn't affect original computation
134
               fork->update(&input[1], input.size() - 2);
6,610✔
135

136
               size_t so_far = 1;
6,610✔
137
               while(so_far < input.size()) {
54,769✔
138
                  size_t take = this->rng().next_byte() % (input.size() - so_far);
48,159✔
139

140
                  if(input.size() - so_far == 1) {
48,159✔
141
                     take = 1;
6,610✔
142
                  }
143

144
                  hash->update(&input[so_far], take);
48,159✔
145
                  so_far += take;
48,159✔
146
               }
147
               result.test_eq(provider, "hashing split", hash->final(), expected);
13,220✔
148

149
               fork->update(&input[input.size() - 1], 1);
6,610✔
150
               result.test_eq(provider, "hashing split", fork->final(), expected);
19,830✔
151
            }
6,610✔
152

153
            if(hash->hash_block_size() > 0) {
6,899✔
154
               // GOST-34.11 uses 32 byte block
155
               result.test_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32);
13,586✔
156
            }
157
         }
20,697✔
158

159
         result.end_timer();
6,899✔
160
         return result;
161
      }
20,697✔
162
};
163

164
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("hash", "hash_algos", Hash_Function_Tests);
165

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

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

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

179
         Test::Result result("NIST Monte Carlo " + algo);
7✔
180
         result.start_timer();
7✔
181

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

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

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

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

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

202
            std::vector<uint8_t> buf(hash->output_length());
6✔
203

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

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

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

221
            result.test_eq("Output is expected", input[2], expected);
12✔
222
         }
12✔
223

224
         result.end_timer();
6✔
225
         return result;
226
      }
21✔
227
};
228

229
BOTAN_REGISTER_TEST("hash", "hash_nist_mc", Hash_NIST_MonteCarlo_Tests);
230

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

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

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

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

248
         return output;
18✔
249
      }
×
250

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

256
         Test::Result result("Long input " + algo);
18✔
257
         result.start_timer();
18✔
258

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

261
         if(total_len > 1000000 && Test::run_long_tests() == false) {
18✔
262
            return result;
263
         }
264

265
         if(providers.empty()) {
18✔
266
            result.note_missing("hash " + algo);
×
267
            return result;
×
268
         }
269

270
         for(const auto& provider_ask : providers) {
36✔
271
            auto hash = Botan::HashFunction::create(algo, provider_ask);
18✔
272

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

278
            const size_t full_inputs = total_len / input.size();
18✔
279
            const size_t leftover = total_len % input.size();
18✔
280

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

285
            if(leftover > 0) {
18✔
286
               hash->update(input.data(), leftover);
9✔
287
            }
288

289
            std::vector<uint8_t> output(hash->output_length());
18✔
290
            hash->final(output.data());
18✔
291
            result.test_eq("Output is expected", output, expected);
36✔
292
         }
36✔
293

294
         result.end_timer();
18✔
295
         return result;
296
      }
54✔
297
};
298

299
BOTAN_REGISTER_TEST("hash", "hash_rep", Hash_LongRepeat_Tests);
300

301
   #if defined(BOTAN_HAS_TRUNCATED_HASH) && defined(BOTAN_HAS_SHA2_32)
302

303
/// negative tests for Truncated_Hash, positive tests are implemented in hash/truncated.vec
304
Test::Result hash_truncation_negative_tests() {
1✔
305
   Test::Result result("hash truncation parameter validation");
1✔
306
   result.start_timer();
1✔
307

308
   result.test_throws<Botan::Invalid_Argument>("truncation to zero",
2✔
309
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,0)"); });
1✔
310
   result.test_throws<Botan::Invalid_Argument>("cannot output more bits than the underlying hash",
2✔
311
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,257)"); });
1✔
312
   auto unobtainable = Botan::HashFunction::create("Truncated(NonExistentHash-256,128)");
1✔
313
   result.confirm("non-existent hashes are not created", unobtainable == nullptr);
2✔
314
   
315
   result.end_timer();
1✔
316
   return result;
1✔
317
}
1✔
318

319
BOTAN_REGISTER_TEST_FN("hash", "hash_truncation", hash_truncation_negative_tests);
320

321
   #endif
322

323
}  // namespace
324

325
#endif
326

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

© 2025 Coveralls, Inc