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

randombit / botan / 5111374265

29 May 2023 11:19AM UTC coverage: 92.227% (+0.5%) from 91.723%
5111374265

push

github

randombit
Next release will be 3.1.0. Update release notes

75588 of 81959 relevant lines covered (92.23%)

11886470.91 hits per line

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

89.41
/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
#endif
12

13
namespace Botan_Tests {
14

15
#if defined(BOTAN_HAS_HASH)
16

17
namespace {
18

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

30
         return {result};
3✔
31
      }
1✔
32

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

52
BOTAN_REGISTER_TEST("hash", "invalid_name_hash", Invalid_Hash_Name_Tests);
53

54
class Hash_Function_Tests final : public Text_Based_Test {
×
55
   public:
56
      Hash_Function_Tests() : Text_Based_Test("hash", "In,Out") {}
2✔
57

58
      std::vector<std::string> possible_providers(const std::string& algo) override {
6,170✔
59
         return provider_filter(Botan::HashFunction::providers(algo));
6,170✔
60
      }
61

62
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
6,170✔
63
         const std::vector<uint8_t> input = vars.get_req_bin("In");
6,170✔
64
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
6,170✔
65

66
         Test::Result result(algo);
12,340✔
67

68
         const std::vector<std::string> providers = possible_providers(algo);
6,170✔
69

70
         if(providers.empty()) {
6,170✔
71
            result.note_missing("hash " + algo);
×
72
            return result;
×
73
         }
74

75
         for(const auto& provider_ask : providers) {
12,340✔
76
            auto hash = Botan::HashFunction::create(algo, provider_ask);
6,170✔
77

78
            if(!hash) {
6,170✔
79
               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
×
80
               continue;
×
81
            }
82

83
            auto clone = hash->new_object();
6,170✔
84

85
            const std::string provider(hash->provider());
6,170✔
86
            result.test_is_nonempty("provider", provider);
6,170✔
87
            result.test_eq(provider, hash->name(), algo);
6,170✔
88
            result.test_eq(provider, hash->name(), clone->name());
12,340✔
89

90
            for(size_t i = 0; i != 3; ++i) {
24,680✔
91
               hash->update(input);
18,510✔
92
               result.test_eq(provider, "hashing", hash->final(), expected);
55,530✔
93
            }
94

95
            clone->update(input);
6,170✔
96
            result.test_eq(provider, "hashing (clone)", clone->final(), expected);
12,340✔
97

98
            // Test to make sure clear() resets what we need it to
99
            hash->update("some discarded input");
6,170✔
100
            hash->clear();
6,170✔
101
            hash->update(nullptr, 0);  // this should be effectively ignored
6,170✔
102
            hash->update(input);
6,170✔
103

104
            result.test_eq(provider, "hashing after clear", hash->final(), expected);
12,340✔
105

106
            // Test that misaligned inputs work
107

108
            if(!input.empty()) {
6,170✔
109
               std::vector<uint8_t> misaligned = input;
6,121✔
110
               const size_t current_alignment = reinterpret_cast<uintptr_t>(misaligned.data()) % 16;
6,121✔
111

112
               const size_t bytes_to_misalign = 15 - current_alignment;
6,121✔
113

114
               for(size_t i = 0; i != bytes_to_misalign; ++i)
97,936✔
115
                  misaligned.insert(misaligned.begin(), 0x23);
91,815✔
116

117
               hash->update(&misaligned[bytes_to_misalign], input.size());
6,121✔
118
               result.test_eq(provider, "hashing misaligned data", hash->final(), expected);
18,363✔
119
            }
6,121✔
120

121
            if(input.size() > 5) {
6,170✔
122
               hash->update(input[0]);
5,905✔
123

124
               auto fork = hash->copy_state();
5,905✔
125
               // verify fork copy doesn't affect original computation
126
               fork->update(&input[1], input.size() - 2);
5,905✔
127

128
               size_t so_far = 1;
129
               while(so_far < input.size()) {
49,190✔
130
                  size_t take = Test::rng().next_byte() % (input.size() - so_far);
43,285✔
131

132
                  if(input.size() - so_far == 1)
43,285✔
133
                     take = 1;
5,905✔
134

135
                  hash->update(&input[so_far], take);
43,285✔
136
                  so_far += take;
43,285✔
137
               }
138
               result.test_eq(provider, "hashing split", hash->final(), expected);
11,810✔
139

140
               fork->update(&input[input.size() - 1], 1);
5,905✔
141
               result.test_eq(provider, "hashing split", fork->final(), expected);
17,715✔
142
            }
5,905✔
143

144
            if(hash->hash_block_size() > 0) {
6,170✔
145
               // GOST-34.11 uses 32 byte block
146
               result.test_gte("If hash_block_size is set, it is large", hash->hash_block_size(), 32);
12,128✔
147
            }
148
         }
18,510✔
149

150
         return result;
151
      }
18,510✔
152
};
153

154
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("hash", "hash_algos", Hash_Function_Tests);
155

156
class Hash_NIST_MonteCarlo_Tests final : public Text_Based_Test {
×
157
   public:
158
      Hash_NIST_MonteCarlo_Tests() : Text_Based_Test("hash_mc.vec", "Seed,Count,Output") {}
3✔
159

160
      std::vector<std::string> possible_providers(const std::string& algo) override {
7✔
161
         return provider_filter(Botan::HashFunction::providers(algo));
7✔
162
      }
163

164
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
7✔
165
         const std::vector<uint8_t> seed = vars.get_req_bin("Seed");
7✔
166
         const size_t count = vars.get_req_sz("Count");
7✔
167
         const std::vector<uint8_t> expected = vars.get_req_bin("Output");
7✔
168

169
         Test::Result result("NIST Monte Carlo " + algo);
7✔
170

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

173
         if(providers.empty()) {
7✔
174
            result.note_missing("hash " + algo);
1✔
175
            return result;
1✔
176
         }
177

178
         for(const auto& provider_ask : providers) {
12✔
179
            auto hash = Botan::HashFunction::create(algo, provider_ask);
6✔
180

181
            if(!hash) {
6✔
182
               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
×
183
               continue;
×
184
            }
185

186
            std::vector<std::vector<uint8_t>> input;
6✔
187
            input.push_back(seed);
6✔
188
            input.push_back(seed);
6✔
189
            input.push_back(seed);
6✔
190

191
            std::vector<uint8_t> buf(hash->output_length());
6✔
192

193
            for(size_t j = 0; j <= count; ++j) {
606✔
194
               for(size_t i = 3; i != 1003; ++i) {
600,600✔
195
                  hash->update(input[0]);
600,000✔
196
                  hash->update(input[1]);
600,000✔
197
                  hash->update(input[2]);
600,000✔
198

199
                  hash->final(input[0].data());
600,000✔
200
                  input[0].swap(input[1]);
600,000✔
201
                  input[1].swap(input[2]);
600,000✔
202
               }
203

204
               if(j < count) {
600✔
205
                  input[0] = input[2];
594✔
206
                  input[1] = input[2];
594✔
207
               }
208
            }
209

210
            result.test_eq("Output is expected", input[2], expected);
12✔
211
         }
12✔
212

213
         return result;
214
      }
21✔
215
};
216

217
BOTAN_REGISTER_TEST("hash", "hash_nist_mc", Hash_NIST_MonteCarlo_Tests);
218

219
class Hash_LongRepeat_Tests final : public Text_Based_Test {
×
220
   public:
221
      Hash_LongRepeat_Tests() : Text_Based_Test("hash_rep.vec", "Input,TotalLength,Digest") {}
3✔
222

223
      std::vector<std::string> possible_providers(const std::string& algo) override {
18✔
224
         return provider_filter(Botan::HashFunction::providers(algo));
18✔
225
      }
226

227
      // repeating the output several times reduces buffering overhead during processing
228
      static std::vector<uint8_t> expand_input(const std::vector<uint8_t>& input, size_t min_len) {
18✔
229
         std::vector<uint8_t> output;
18✔
230
         output.reserve(min_len);
18✔
231

232
         while(output.size() < min_len)
2,358✔
233
            output.insert(output.end(), input.begin(), input.end());
2,340✔
234

235
         return output;
18✔
236
      }
×
237

238
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
18✔
239
         const std::vector<uint8_t> input = expand_input(vars.get_req_bin("Input"), 256);
36✔
240
         const size_t total_len = vars.get_req_sz("TotalLength");
18✔
241
         const std::vector<uint8_t> expected = vars.get_req_bin("Digest");
18✔
242

243
         Test::Result result("Long input " + algo);
18✔
244

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

247
         if(total_len > 1000000 && Test::run_long_tests() == false) {
18✔
248
            return result;
249
         }
250

251
         if(providers.empty()) {
18✔
252
            result.note_missing("hash " + algo);
×
253
            return result;
×
254
         }
255

256
         for(const auto& provider_ask : providers) {
36✔
257
            auto hash = Botan::HashFunction::create(algo, provider_ask);
18✔
258

259
            if(!hash) {
18✔
260
               result.test_failure("Hash " + algo + " supported by " + provider_ask + " but not found");
×
261
               continue;
×
262
            }
263

264
            const size_t full_inputs = total_len / input.size();
18✔
265
            const size_t leftover = total_len % input.size();
18✔
266

267
            for(size_t i = 0; i != full_inputs; ++i)
37,783,908✔
268
               hash->update(input);
37,783,890✔
269

270
            if(leftover > 0)
18✔
271
               hash->update(input.data(), leftover);
9✔
272

273
            std::vector<uint8_t> output(hash->output_length());
18✔
274
            hash->final(output.data());
18✔
275
            result.test_eq("Output is expected", output, expected);
36✔
276
         }
36✔
277

278
         return result;
279
      }
54✔
280
};
281

282
BOTAN_REGISTER_TEST("hash", "hash_rep", Hash_LongRepeat_Tests);
283

284
   #if defined(BOTAN_HAS_TRUNCATED_HASH) && defined(BOTAN_HAS_SHA2_32)
285

286
/// negative tests for Truncated_Hash, positive tests are implemented in hash/truncated.vec
287
Test::Result hash_truncation_negative_tests() {
1✔
288
   Test::Result result("hash truncation parameter validation");
1✔
289
   result.test_throws<Botan::Invalid_Argument>("truncation to zero",
2✔
290
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,0)"); });
1✔
291
   result.test_throws<Botan::Invalid_Argument>("cannot output more bits than the underlying hash",
2✔
292
                                               [] { Botan::HashFunction::create("Truncated(SHA-256,257)"); });
1✔
293
   auto unobtainable = Botan::HashFunction::create("Truncated(NonExistentHash-256,128)");
1✔
294
   result.confirm("non-existent hashes are not created", unobtainable == nullptr);
2✔
295
   return result;
1✔
296
}
1✔
297

298
BOTAN_REGISTER_TEST_FN("hash", "hash_truncation", hash_truncation_negative_tests);
299

300
   #endif
301

302
}  // namespace
303

304
#endif
305

306
}  // 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