• 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

94.49
/src/tests/test_aead.cpp
1
/*
2
* (C) 2014,2015,2016,2018 Jack Lloyd
3
* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_AEAD_MODES)
11
   #include <botan/aead.h>
12
   #include <botan/mem_ops.h>
13
#endif
14

15
namespace Botan_Tests {
16

17
namespace {
18

19
#if defined(BOTAN_HAS_AEAD_MODES)
20

21
class AEAD_Tests final : public Text_Based_Test {
×
22
   public:
23
      AEAD_Tests() : Text_Based_Test("aead", "Key,In,Out", "Nonce,AD") {}
2✔
24

25
      static Test::Result test_enc(const std::vector<uint8_t>& key,
2,348✔
26
                                   const std::vector<uint8_t>& nonce,
27
                                   const std::vector<uint8_t>& input,
28
                                   const std::vector<uint8_t>& expected,
29
                                   const std::vector<uint8_t>& ad,
30
                                   const std::string& algo,
31
                                   Botan::RandomNumberGenerator& rng) {
32
         const bool is_siv = algo.find("/SIV") != std::string::npos;
2,348✔
33

34
         Test::Result result(algo);
2,348✔
35
         result.start_timer();
2,348✔
36

37
         auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption);
2,348✔
38

39
         result.test_eq("AEAD encrypt output_length is correct", enc->output_length(input.size()), expected.size());
2,348✔
40

41
         result.confirm("AEAD name is not empty", !enc->name().empty());
4,696✔
42
         result.confirm("AEAD default nonce size is accepted", enc->valid_nonce_length(enc->default_nonce_length()));
4,696✔
43

44
         Botan::secure_vector<uint8_t> garbage = rng.random_vec(enc->update_granularity());
2,348✔
45

46
         if(is_siv == false) {
2,348✔
47
            result.test_throws("Unkeyed object throws for encrypt", [&]() { enc->update(garbage); });
8,104✔
48
         }
49

50
         result.test_throws("Unkeyed object throws for encrypt", [&]() { enc->finish(garbage); });
7,044✔
51

52
         if(enc->associated_data_requires_key()) {
2,348✔
53
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
54
                               [&]() { enc->set_associated_data(ad.data(), ad.size()); });
1,478✔
55
         }
56

57
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,348✔
58

59
         // Ensure that test resets AD and message state
60
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,348✔
61
         enc->set_key(key);
2,348✔
62
         result.test_eq("key is set", enc->has_keying_material(), true);
2,348✔
63

64
         if(is_siv == false) {
2,348✔
65
            result.test_throws("Cannot process data until nonce is set (enc)", [&]() { enc->update(garbage); });
6,078✔
66
            result.test_throws("Cannot process data until nonce is set (enc)", [&]() { enc->finish(garbage); });
8,104✔
67
         }
68

69
         enc->set_associated_data(mutate_vec(ad, rng));
2,348✔
70
         enc->start(mutate_vec(nonce, rng));
2,348✔
71
         enc->update(garbage);
2,348✔
72

73
         // reset message specific state
74
         enc->reset();
2,348✔
75

76
         /*
77
         Now try to set the AD *after* setting the nonce
78
         For some modes this works, for others it does not.
79
         */
80
         enc->start(nonce);
2,348✔
81

82
         try {
2,348✔
83
            enc->set_associated_data(ad);
2,348✔
84
         } catch(Botan::Invalid_State&) {
1,946✔
85
            // ad after setting nonce rejected, in this case we need to reset
86
            enc->reset();
1,946✔
87
            enc->set_associated_data(ad);
1,946✔
88
            enc->start(nonce);
1,946✔
89
         }
1,946✔
90

91
         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
2,348✔
92

93
         // have to check here first if input is empty if not we can test update() and eventually process()
94
         if(buf.empty()) {
2,348✔
95
            enc->finish(buf);
76✔
96
            result.test_eq("encrypt with empty input", buf, expected);
152✔
97
         } else {
98
            // test finish() with full input
99
            enc->finish(buf);
2,272✔
100
            result.test_eq("encrypt full", buf, expected);
2,272✔
101

102
            // additionally test update() if possible
103
            const size_t update_granularity = enc->update_granularity();
2,272✔
104
            if(input.size() > update_granularity) {
2,272✔
105
               // reset state first
106
               enc->reset();
2,181✔
107

108
               enc->set_associated_data(ad);
2,181✔
109
               enc->start(nonce);
2,181✔
110

111
               buf.assign(input.begin(), input.end());
2,181✔
112
               size_t input_length = buf.size();
2,181✔
113
               uint8_t* p = buf.data();
2,181✔
114
               Botan::secure_vector<uint8_t> block(update_granularity);
2,181✔
115
               Botan::secure_vector<uint8_t> ciphertext;
2,181✔
116
               ciphertext.reserve(enc->output_length(buf.size()));
2,181✔
117
               while(input_length > update_granularity &&
210,101✔
118
                     ((input_length - update_granularity) >= enc->minimum_final_size())) {
103,960✔
119
                  block.assign(p, p + update_granularity);
103,960✔
120
                  enc->update(block);
103,960✔
121
                  p += update_granularity;
103,960✔
122
                  input_length -= update_granularity;
103,960✔
123

124
                  ciphertext.insert(ciphertext.end(), block.begin(), block.end());
103,960✔
125
               }
126

127
               // encrypt remaining bytes
128
               block.assign(p, p + input_length);
2,181✔
129
               enc->finish(block);
2,181✔
130
               ciphertext.insert(ciphertext.end(), block.begin(), block.end());
2,181✔
131

132
               result.test_eq("encrypt update", ciphertext, expected);
4,362✔
133
            }
4,362✔
134

135
            // additionally test process() if possible
136
            size_t min_final_bytes = enc->minimum_final_size();
2,272✔
137
            if(input.size() > (update_granularity + min_final_bytes)) {
2,272✔
138
               // again reset state first
139
               enc->reset();
2,181✔
140

141
               enc->set_associated_data(ad);
2,181✔
142
               enc->start(nonce);
2,181✔
143

144
               buf.assign(input.begin(), input.end());
2,181✔
145

146
               // we can process at max input.size()
147
               const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity;
2,181✔
148
               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
2,181✔
149

150
               const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
2,181✔
151

152
               result.confirm("Process returns data unless requires_entire_message",
4,362✔
153
                              enc->requires_entire_message(),
2,181✔
154
                              bytes_written == 0);
155

156
               if(bytes_written == 0) {
2,181✔
157
                  // SIV case
158
                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
339✔
159
                  enc->finish(buf);
339✔
160
               } else {
161
                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
1,842✔
162
                  enc->finish(buf, bytes_written);
1,842✔
163
               }
164

165
               result.test_eq("encrypt process", buf, expected);
4,362✔
166
            }
167
         }
168

169
         // Make sure we can set the AD after processing a message
170
         enc->set_associated_data(ad);
2,348✔
171
         enc->clear();
2,348✔
172
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,348✔
173

174
         result.test_throws("Unkeyed object throws for encrypt after clear", [&]() { enc->finish(buf); });
7,044✔
175

176
         if(enc->associated_data_requires_key()) {
2,348✔
177
            result.test_throws("Unkeyed object throws for set AD after clear",
2,217✔
178
                               [&]() { enc->set_associated_data(ad.data(), ad.size()); });
1,478✔
179
         }
180

181
         result.end_timer();
2,348✔
182
         return result;
2,348✔
183
      }
7,044✔
184

185
      static Test::Result test_dec(const std::vector<uint8_t>& key,
2,348✔
186
                                   const std::vector<uint8_t>& nonce,
187
                                   const std::vector<uint8_t>& input,
188
                                   const std::vector<uint8_t>& expected,
189
                                   const std::vector<uint8_t>& ad,
190
                                   const std::string& algo,
191
                                   Botan::RandomNumberGenerator& rng) {
192
         const bool is_siv = algo.find("/SIV") != std::string::npos;
2,348✔
193

194
         Test::Result result(algo);
2,348✔
195
         result.start_timer();
2,348✔
196

197
         auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption);
2,348✔
198

199
         result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size());
2,348✔
200

201
         Botan::secure_vector<uint8_t> garbage = rng.random_vec(dec->update_granularity());
2,348✔
202

203
         if(is_siv == false) {
2,348✔
204
            result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->update(garbage); });
8,104✔
205
         }
206

207
         result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->finish(garbage); });
7,044✔
208

209
         if(dec->associated_data_requires_key()) {
2,348✔
210
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
211
                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
1,478✔
212
         }
213

214
         // First some tests for reset() to make sure it resets what we need it to
215
         // set garbage values
216
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,348✔
217
         dec->set_key(key);
2,348✔
218
         result.test_eq("key is set", dec->has_keying_material(), true);
2,348✔
219
         dec->set_associated_data(mutate_vec(ad, rng));
2,348✔
220

221
         if(is_siv == false) {
2,348✔
222
            result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->update(garbage); });
6,078✔
223
            result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->finish(garbage); });
8,104✔
224
         }
225

226
         dec->start(mutate_vec(nonce, rng));
2,348✔
227

228
         dec->update(garbage);
2,348✔
229

230
         // reset message specific state
231
         dec->reset();
2,348✔
232

233
         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
2,348✔
234
         try {
2,348✔
235
            // now try to decrypt with correct values
236

237
            try {
2,348✔
238
               dec->start(nonce);
2,348✔
239
               dec->set_associated_data(ad);
2,348✔
240
            } catch(Botan::Invalid_State&) {
1,946✔
241
               // ad after setting nonce rejected, in this case we need to reset
242
               dec->reset();
1,946✔
243
               dec->set_associated_data(ad);
1,946✔
244
               dec->start(nonce);
1,946✔
245
            }
1,946✔
246

247
            // test finish() with full input
248
            dec->finish(buf);
2,348✔
249
            result.test_eq("decrypt full", buf, expected);
2,348✔
250

251
            // additionally test update() if possible
252
            const size_t update_granularity = dec->update_granularity();
2,348✔
253
            if(input.size() > update_granularity) {
2,348✔
254
               // reset state first
255
               dec->reset();
2,321✔
256

257
               dec->set_associated_data(ad);
2,321✔
258
               dec->start(nonce);
2,321✔
259

260
               buf.assign(input.begin(), input.end());
2,321✔
261
               size_t input_length = buf.size();
2,321✔
262
               uint8_t* p = buf.data();
2,321✔
263
               Botan::secure_vector<uint8_t> block(update_granularity);
2,321✔
264
               Botan::secure_vector<uint8_t> plaintext;
2,321✔
265
               plaintext.reserve(dec->output_length(buf.size()));
2,321✔
266
               while((input_length > update_granularity) &&
216,782✔
267
                     ((input_length - update_granularity) >= dec->minimum_final_size())) {
108,353✔
268
                  block.assign(p, p + update_granularity);
106,108✔
269
                  dec->update(block);
106,108✔
270
                  p += update_granularity;
106,108✔
271
                  input_length -= update_granularity;
106,108✔
272
                  plaintext.insert(plaintext.end(), block.begin(), block.end());
106,108✔
273
               }
274

275
               // decrypt remaining bytes
276
               block.assign(p, p + input_length);
2,321✔
277
               dec->finish(block);
2,321✔
278
               plaintext.insert(plaintext.end(), block.begin(), block.end());
2,321✔
279

280
               result.test_eq("decrypt update", plaintext, expected);
4,642✔
281
            }
4,642✔
282

283
            // additionally test process() if possible
284
            const size_t min_final_size = dec->minimum_final_size();
2,348✔
285
            if(input.size() > (update_granularity + min_final_size)) {
2,348✔
286
               // again reset state first
287
               dec->reset();
2,181✔
288

289
               dec->set_associated_data(ad);
2,181✔
290
               dec->start(nonce);
2,181✔
291

292
               buf.assign(input.begin(), input.end());
2,181✔
293

294
               // we can process at max input.size()
295
               const size_t max_blocks_to_process = (input.size() - min_final_size) / update_granularity;
2,181✔
296
               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
2,181✔
297

298
               const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
2,181✔
299

300
               result.confirm("Process returns data unless requires_entire_message",
4,362✔
301
                              dec->requires_entire_message(),
2,181✔
302
                              bytes_written == 0);
303

304
               if(bytes_written == 0) {
2,181✔
305
                  // SIV case
306
                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
339✔
307
                  dec->finish(buf);
339✔
308
               } else {
309
                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
1,842✔
310
                  dec->finish(buf, bytes_to_process);
1,842✔
311
               }
312

313
               result.test_eq("decrypt process", buf, expected);
4,362✔
314
            }
315

316
         } catch(Botan::Exception& e) {
×
317
            result.test_failure("Failure processing AEAD ciphertext", e.what());
×
318
         }
×
319

320
         // test decryption with modified ciphertext
321
         const std::vector<uint8_t> mutated_input = mutate_vec(input, rng, true);
2,348✔
322
         buf.assign(mutated_input.begin(), mutated_input.end());
2,348✔
323

324
         dec->reset();
2,348✔
325

326
         dec->set_associated_data(ad);
2,348✔
327
         dec->start(nonce);
2,348✔
328

329
         try {
2,348✔
330
            dec->finish(buf);
2,348✔
331
            result.test_failure("accepted modified message", mutated_input);
×
332
         } catch(Botan::Integrity_Failure&) {
2,348✔
333
            result.test_success("correctly rejected modified message");
2,348✔
334
         } catch(std::exception& e) {
2,348✔
335
            result.test_failure("unexpected error while rejecting modified message", e.what());
×
336
         }
×
337

338
         // test decryption with modified nonce
339
         if(!nonce.empty()) {
2,348✔
340
            buf.assign(input.begin(), input.end());
2,022✔
341
            std::vector<uint8_t> bad_nonce = mutate_vec(nonce, rng);
2,022✔
342

343
            dec->reset();
2,022✔
344
            dec->set_associated_data(ad);
2,022✔
345
            dec->start(bad_nonce);
2,022✔
346

347
            try {
2,022✔
348
               dec->finish(buf);
2,022✔
349
               result.test_failure("accepted message with modified nonce", bad_nonce);
×
350
            } catch(Botan::Integrity_Failure&) {
2,022✔
351
               result.test_success("correctly rejected modified nonce");
2,022✔
352
            } catch(std::exception& e) {
2,022✔
353
               result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
354
            }
×
355
         }
2,022✔
356

357
         // test decryption with modified associated_data
358
         const std::vector<uint8_t> bad_ad = mutate_vec(ad, rng, true);
2,348✔
359

360
         dec->reset();
2,348✔
361
         dec->set_associated_data(bad_ad);
2,348✔
362

363
         dec->start(nonce);
2,348✔
364

365
         try {
2,348✔
366
            buf.assign(input.begin(), input.end());
2,348✔
367
            dec->finish(buf);
2,348✔
368
            result.test_failure("accepted message with modified ad", bad_ad);
×
369
         } catch(Botan::Integrity_Failure&) {
2,348✔
370
            result.test_success("correctly rejected modified ad");
2,348✔
371
         } catch(std::exception& e) {
2,348✔
372
            result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
373
         }
×
374

375
         // Make sure we can set the AD after processing a message
376
         dec->set_associated_data(ad);
2,348✔
377
         dec->clear();
2,348✔
378
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,348✔
379

380
         result.test_throws("Unkeyed object throws for decrypt", [&]() { dec->finish(buf); });
7,044✔
381

382
         if(dec->associated_data_requires_key()) {
2,348✔
383
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
384
                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
1,478✔
385
         }
386

387
         result.end_timer();
2,348✔
388
         return result;
2,348✔
389
      }
11,740✔
390

391
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
2,348✔
392
         const std::vector<uint8_t> key = vars.get_req_bin("Key");
2,348✔
393
         const std::vector<uint8_t> nonce = vars.get_opt_bin("Nonce");
2,348✔
394
         const std::vector<uint8_t> input = vars.get_req_bin("In");
2,348✔
395
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
2,348✔
396
         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
2,348✔
397

398
         Test::Result result(algo);
4,696✔
399
         result.start_timer();
2,348✔
400

401
         auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption);
2,348✔
402
         auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption);
2,348✔
403

404
         if(!enc || !dec) {
2,348✔
405
            result.note_missing(algo);
×
406
            return result;
407
         }
408

409
         // must be authenticated
410
         result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true);
2,348✔
411
         result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true);
2,348✔
412

413
         const std::string enc_provider = enc->provider();
2,348✔
414
         result.test_is_nonempty("enc provider", enc_provider);
2,348✔
415
         const std::string dec_provider = enc->provider();
2,348✔
416
         result.test_is_nonempty("dec provider", dec_provider);
2,348✔
417

418
         result.test_eq("same provider", enc_provider, dec_provider);
2,348✔
419

420
         // FFI currently requires this, so assure it is true for all modes
421
         result.test_gt("enc buffer sizes ok", enc->ideal_granularity(), enc->minimum_final_size());
2,348✔
422
         result.test_gt("dec buffer sizes ok", dec->ideal_granularity(), dec->minimum_final_size());
2,348✔
423

424
         result.test_gt("update granularity is non-zero", enc->update_granularity(), 0);
2,348✔
425

426
         result.test_eq(
2,348✔
427
            "enc and dec ideal granularity is the same", enc->ideal_granularity(), dec->ideal_granularity());
2,348✔
428

429
         result.test_gt(
2,348✔
430
            "ideal granularity is at least update granularity", enc->ideal_granularity(), enc->update_granularity());
2,348✔
431

432
         result.confirm("ideal granularity is a multiple of update granularity",
4,696✔
433
                        enc->ideal_granularity() % enc->update_granularity() == 0);
2,348✔
434

435
         // test enc
436
         result.merge(test_enc(key, nonce, input, expected, ad, algo, this->rng()));
2,348✔
437

438
         // test dec
439
         result.merge(test_dec(key, nonce, expected, input, ad, algo, this->rng()));
2,348✔
440

441
         result.end_timer();
2,348✔
442
         return result;
2,348✔
443
      }
18,344✔
444
};
445

446
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("modes", "aead", AEAD_Tests);
447

448
#endif
449

450
}  // namespace
451

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