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

randombit / botan / 19012754211

02 Nov 2025 01:10PM UTC coverage: 90.677% (+0.006%) from 90.671%
19012754211

push

github

web-flow
Merge pull request #5137 from randombit/jack/clang-tidy-includes

Remove various unused includes flagged by clang-tidy misc-include-cleaner

100457 of 110786 relevant lines covered (90.68%)

12189873.8 hits per line

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

92.03
/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
#endif
13

14
namespace Botan_Tests {
15

16
namespace {
17

18
#if defined(BOTAN_HAS_AEAD_MODES)
19

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

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

33
         Test::Result result(algo);
2,353✔
34

35
         auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption);
2,353✔
36

37
         result.test_eq("AEAD encrypt output_length is correct", enc->output_length(input.size()), expected.size());
2,353✔
38

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

42
         auto get_garbage = [&] { return rng.random_vec(enc->update_granularity()); };
10,799✔
43

44
         if(!is_siv) {
2,353✔
45
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for encrypt", [&]() {
6,093✔
46
               auto garbage = get_garbage();
2,031✔
47
               enc->update(garbage);
2,031✔
48
            });
×
49
         }
50

51
         result.test_throws<Botan::Invalid_State>("Unkeyed object throws for encrypt", [&]() {
4,706✔
52
            auto garbage = get_garbage();
2,353✔
53
            enc->finish(garbage);
2,353✔
54
         });
×
55

56
         if(enc->associated_data_requires_key()) {
2,353✔
57
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for set AD",
2,229✔
58
                                                     [&]() { enc->set_associated_data(ad.data(), ad.size()); });
1,486✔
59
         }
60

61
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,353✔
62

63
         // Ensure that test resets AD and message state
64
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,353✔
65
         enc->set_key(key);
2,353✔
66
         result.test_eq("key is set", enc->has_keying_material(), true);
2,353✔
67

68
         if(!is_siv) {
2,353✔
69
            result.test_throws<Botan::Invalid_State>("Cannot process data until nonce is set (enc)", [&]() {
4,062✔
70
               auto garbage = get_garbage();
2,031✔
71
               enc->update(garbage);
2,031✔
72
            });
×
73
            result.test_throws<Botan::Invalid_State>("Cannot process data until nonce is set (enc)", [&]() {
6,093✔
74
               auto garbage = get_garbage();
2,031✔
75
               enc->finish(garbage);
2,031✔
76
            });
×
77
         }
78

79
         enc->set_associated_data(mutate_vec(ad, rng));
2,353✔
80
         enc->start(mutate_vec(nonce, rng));
2,353✔
81

82
         auto garbage = get_garbage();
2,353✔
83
         enc->update(garbage);
2,353✔
84

85
         // reset message specific state
86
         enc->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,353✔
87

88
         /*
89
         Now try to set the AD *after* setting the nonce
90
         For some modes this works, for others it does not.
91
         */
92
         enc->start(nonce);
2,353✔
93

94
         try {
2,353✔
95
            enc->set_associated_data(ad);
2,353✔
96
         } catch(Botan::Invalid_State&) {
1,950✔
97
            // ad after setting nonce rejected, in this case we need to reset
98
            enc->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
1,950✔
99
            enc->set_associated_data(ad);
1,950✔
100
            enc->start(nonce);
1,950✔
101
         }
1,950✔
102

103
         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
2,353✔
104

105
         // have to check here first if input is empty if not we can test update() and eventually process()
106
         if(buf.empty()) {
2,353✔
107
            enc->finish(buf);
76✔
108
            result.test_eq("encrypt with empty input", buf, expected);
152✔
109
         } else {
110
            // test finish() with full input
111
            enc->finish(buf);
2,277✔
112
            result.test_eq("encrypt full", buf, expected);
4,554✔
113

114
            // additionally test update() if possible
115
            const size_t update_granularity = enc->update_granularity();
2,277✔
116
            if(input.size() > update_granularity) {
2,277✔
117
               // reset state first
118
               enc->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,218✔
119

120
               enc->set_associated_data(ad);
2,218✔
121
               enc->start(nonce);
2,218✔
122

123
               buf.assign(input.begin(), input.end());
2,218✔
124
               size_t input_length = buf.size();
2,218✔
125
               uint8_t* p = buf.data();
2,218✔
126
               Botan::secure_vector<uint8_t> block(update_granularity);
2,218✔
127
               Botan::secure_vector<uint8_t> ciphertext;
2,218✔
128
               ciphertext.reserve(enc->output_length(buf.size()));
2,218✔
129
               while(input_length > update_granularity &&
229,216✔
130
                     ((input_length - update_granularity) >= enc->minimum_final_size())) {
113,499✔
131
                  block.assign(p, p + update_granularity);
113,499✔
132
                  enc->update(block);
113,499✔
133
                  p += update_granularity;
113,499✔
134
                  input_length -= update_granularity;
113,499✔
135

136
                  ciphertext.insert(ciphertext.end(), block.begin(), block.end());
113,499✔
137
               }
138

139
               // encrypt remaining bytes
140
               block.assign(p, p + input_length);
2,218✔
141
               enc->finish(block);
2,218✔
142
               ciphertext.insert(ciphertext.end(), block.begin(), block.end());
2,218✔
143

144
               result.test_eq("encrypt update", ciphertext, expected);
4,436✔
145
            }
4,436✔
146

147
            // additionally test process() if possible
148
            size_t min_final_bytes = enc->minimum_final_size();
2,277✔
149
            if(input.size() > (update_granularity + min_final_bytes)) {
2,277✔
150
               // again reset state first
151
               enc->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,218✔
152

153
               enc->set_associated_data(ad);
2,218✔
154
               enc->start(nonce);
2,218✔
155

156
               buf.assign(input.begin(), input.end());
2,218✔
157

158
               // we can process at max input.size()
159
               const size_t max_blocks_to_process = (input.size() - min_final_bytes) / update_granularity;
2,218✔
160
               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
2,218✔
161

162
               const size_t bytes_written = enc->process(buf.data(), bytes_to_process);
2,218✔
163

164
               result.confirm("Process returns data unless requires_entire_message",
4,436✔
165
                              enc->requires_entire_message(),
2,218✔
166
                              bytes_written == 0);
167

168
               if(bytes_written == 0) {
2,218✔
169
                  // SIV case
170
                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
340✔
171
                  enc->finish(buf);
340✔
172
               } else {
173
                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
1,878✔
174
                  enc->finish(buf, bytes_written);
1,878✔
175
               }
176

177
               result.test_eq("encrypt process", buf, expected);
4,436✔
178
            }
179
         }
180

181
         // Make sure we can set the AD after processing a message
182
         enc->set_associated_data(ad);
2,353✔
183
         enc->clear();
2,353✔
184
         result.test_eq("key is not set", enc->has_keying_material(), false);
2,353✔
185

186
         result.test_throws<Botan::Invalid_State>("Unkeyed object throws for encrypt after clear",
4,706✔
187
                                                  [&]() { enc->finish(buf); });
4,706✔
188

189
         if(enc->associated_data_requires_key()) {
2,353✔
190
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for set AD after clear",
2,229✔
191
                                                     [&]() { enc->set_associated_data(ad.data(), ad.size()); });
1,486✔
192
         }
193

194
         return result;
2,353✔
195
      }
7,059✔
196

197
      static Test::Result test_dec(const std::vector<uint8_t>& key,
2,353✔
198
                                   const std::vector<uint8_t>& nonce,
199
                                   const std::vector<uint8_t>& input,
200
                                   const std::vector<uint8_t>& expected,
201
                                   const std::vector<uint8_t>& ad,
202
                                   const std::string& algo,
203
                                   Botan::RandomNumberGenerator& rng) {
204
         const bool is_siv = algo.find("/SIV") != std::string::npos;
2,353✔
205

206
         Test::Result result(algo);
2,353✔
207

208
         auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption);
2,353✔
209

210
         result.test_eq("AEAD decrypt output_length is correct", dec->output_length(input.size()), expected.size());
2,353✔
211

212
         auto get_garbage = [&] { return rng.random_vec(dec->update_granularity()); };
6,415✔
213
         auto get_ultimate_garbage = [&] { return rng.random_vec(dec->minimum_final_size()); };
4,384✔
214

215
         if(!is_siv) {
2,353✔
216
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for decrypt", [&]() {
6,093✔
217
               auto garbage = get_garbage();
2,031✔
218
               dec->update(garbage);
2,031✔
219
            });
×
220
         }
221

222
         result.test_throws<Botan::Invalid_State>("Unkeyed object throws for decrypt", [&]() {
4,706✔
223
            auto garbage = get_ultimate_garbage();
2,353✔
224
            dec->finish(garbage);
2,353✔
225
         });
×
226

227
         if(dec->associated_data_requires_key()) {
2,353✔
228
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for set AD",
2,229✔
229
                                                     [&]() { dec->set_associated_data(ad.data(), ad.size()); });
1,486✔
230
         }
231

232
         // First some tests for reset() to make sure it resets what we need it to
233
         // set garbage values
234
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,353✔
235
         dec->set_key(key);
2,353✔
236
         result.test_eq("key is set", dec->has_keying_material(), true);
2,353✔
237
         dec->set_associated_data(mutate_vec(ad, rng));
2,353✔
238

239
         if(!is_siv) {
2,353✔
240
            result.test_throws<Botan::Invalid_State>("Cannot process data until nonce is set (dec)", [&]() {
4,062✔
241
               auto garbage = get_garbage();
2,031✔
242
               dec->update(garbage);
2,031✔
243
            });
×
244
            result.test_throws<Botan::Invalid_State>("Cannot process data until nonce is set (dec)", [&]() {
6,093✔
245
               auto garbage = get_ultimate_garbage();
2,031✔
246
               dec->finish(garbage);
2,031✔
247
            });
×
248
         }
249

250
         dec->start(mutate_vec(nonce, rng));
2,353✔
251
         auto garbage = get_garbage();
2,353✔
252
         dec->update(garbage);
2,353✔
253

254
         // reset message specific state
255
         dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,353✔
256

257
         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
2,353✔
258
         try {
2,353✔
259
            // now try to decrypt with correct values
260

261
            try {
2,353✔
262
               dec->start(nonce);
2,353✔
263
               dec->set_associated_data(ad);
2,353✔
264
            } catch(Botan::Invalid_State&) {
1,950✔
265
               // ad after setting nonce rejected, in this case we need to reset
266
               dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
1,950✔
267
               dec->set_associated_data(ad);
1,950✔
268
               dec->start(nonce);
1,950✔
269
            }
1,950✔
270

271
            // test finish() with full input
272
            dec->finish(buf);
2,353✔
273
            result.test_eq("decrypt full", buf, expected);
4,706✔
274

275
            // additionally test update() if possible
276
            const size_t update_granularity = dec->update_granularity();
2,353✔
277
            if(input.size() > update_granularity) {
2,353✔
278
               // reset state first
279
               dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,338✔
280

281
               dec->set_associated_data(ad);
2,338✔
282
               dec->start(nonce);
2,338✔
283

284
               buf.assign(input.begin(), input.end());
2,338✔
285
               size_t input_length = buf.size();
2,338✔
286
               uint8_t* p = buf.data();
2,338✔
287
               Botan::secure_vector<uint8_t> block(update_granularity);
2,338✔
288
               Botan::secure_vector<uint8_t> plaintext;
2,338✔
289
               plaintext.reserve(dec->output_length(buf.size()));
2,338✔
290
               while((input_length > update_granularity) &&
236,176✔
291
                     ((input_length - update_granularity) >= dec->minimum_final_size())) {
118,082✔
292
                  block.assign(p, p + update_granularity);
115,756✔
293
                  dec->update(block);
115,756✔
294
                  p += update_granularity;
115,756✔
295
                  input_length -= update_granularity;
115,756✔
296
                  plaintext.insert(plaintext.end(), block.begin(), block.end());
115,756✔
297
               }
298

299
               // decrypt remaining bytes
300
               block.assign(p, p + input_length);
2,338✔
301
               dec->finish(block);
2,338✔
302
               plaintext.insert(plaintext.end(), block.begin(), block.end());
2,338✔
303

304
               result.test_eq("decrypt update", plaintext, expected);
4,676✔
305
            }
4,676✔
306

307
            // additionally test process() if possible
308
            const size_t min_final_size = dec->minimum_final_size();
2,353✔
309
            if(input.size() > (update_granularity + min_final_size)) {
2,353✔
310
               // again reset state first
311
               dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,218✔
312

313
               dec->set_associated_data(ad);
2,218✔
314
               dec->start(nonce);
2,218✔
315

316
               buf.assign(input.begin(), input.end());
2,218✔
317

318
               // we can process at max input.size()
319
               const size_t max_blocks_to_process = (input.size() - min_final_size) / update_granularity;
2,218✔
320
               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
2,218✔
321

322
               const size_t bytes_written = dec->process(buf.data(), bytes_to_process);
2,218✔
323

324
               result.confirm("Process returns data unless requires_entire_message",
4,436✔
325
                              dec->requires_entire_message(),
2,218✔
326
                              bytes_written == 0);
327

328
               if(bytes_written == 0) {
2,218✔
329
                  // SIV case
330
                  buf.erase(buf.begin(), buf.begin() + bytes_to_process);
340✔
331
                  dec->finish(buf);
340✔
332
               } else {
333
                  result.test_eq("correct number of bytes processed", bytes_written, bytes_to_process);
1,878✔
334
                  dec->finish(buf, bytes_to_process);
1,878✔
335
               }
336

337
               result.test_eq("decrypt process", buf, expected);
4,436✔
338
            }
339

340
         } catch(Botan::Exception& e) {
×
341
            result.test_failure("Failure processing AEAD ciphertext", e.what());
×
342
         }
×
343

344
         // test decryption with modified ciphertext
345
         const std::vector<uint8_t> mutated_input = mutate_vec(input, rng, true);
2,353✔
346
         buf.assign(mutated_input.begin(), mutated_input.end());
2,353✔
347

348
         dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,353✔
349

350
         dec->set_associated_data(ad);
2,353✔
351
         dec->start(nonce);
2,353✔
352

353
         try {
2,353✔
354
            dec->finish(buf);
2,353✔
355
            result.test_failure("accepted modified message", mutated_input);
×
356
         } catch(Botan::Integrity_Failure&) {
2,353✔
357
            result.test_success("correctly rejected modified message");
2,353✔
358
         } catch(std::exception& e) {
2,353✔
359
            result.test_failure("unexpected error while rejecting modified message", e.what());
×
360
         }
×
361

362
         // test decryption with modified nonce
363
         if(!nonce.empty()) {
2,353✔
364
            buf.assign(input.begin(), input.end());
2,027✔
365
            std::vector<uint8_t> bad_nonce = mutate_vec(nonce, rng);
2,027✔
366

367
            dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,027✔
368
            dec->set_associated_data(ad);
2,027✔
369
            dec->start(bad_nonce);
2,027✔
370

371
            try {
2,027✔
372
               dec->finish(buf);
2,027✔
373
               result.test_failure("accepted message with modified nonce", bad_nonce);
×
374
            } catch(Botan::Integrity_Failure&) {
2,027✔
375
               result.test_success("correctly rejected modified nonce");
2,027✔
376
            } catch(std::exception& e) {
2,027✔
377
               result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
378
            }
×
379
         }
2,027✔
380

381
         // test decryption with modified associated_data
382
         const std::vector<uint8_t> bad_ad = mutate_vec(ad, rng, true);
2,353✔
383

384
         dec->reset();  // NOLINT(*-ambiguous-smartptr-reset-call)
2,353✔
385
         dec->set_associated_data(bad_ad);
2,353✔
386

387
         dec->start(nonce);
2,353✔
388

389
         try {
2,353✔
390
            buf.assign(input.begin(), input.end());
2,353✔
391
            dec->finish(buf);
2,353✔
392
            result.test_failure("accepted message with modified ad", bad_ad);
×
393
         } catch(Botan::Integrity_Failure&) {
2,353✔
394
            result.test_success("correctly rejected modified ad");
2,353✔
395
         } catch(std::exception& e) {
2,353✔
396
            result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
397
         }
×
398

399
         // Make sure we can set the AD after processing a message
400
         dec->set_associated_data(ad);
2,353✔
401
         dec->clear();
2,353✔
402
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,353✔
403

404
         result.test_throws<Botan::Invalid_State>("Unkeyed object throws for decrypt", [&]() { dec->finish(buf); });
7,059✔
405

406
         if(dec->associated_data_requires_key()) {
2,353✔
407
            result.test_throws<Botan::Invalid_State>("Unkeyed object throws for set AD",
2,229✔
408
                                                     [&]() { dec->set_associated_data(ad.data(), ad.size()); });
1,486✔
409
         }
410

411
         return result;
2,353✔
412
      }
11,765✔
413

414
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
2,353✔
415
         const std::vector<uint8_t> key = vars.get_req_bin("Key");
2,353✔
416
         const std::vector<uint8_t> nonce = vars.get_opt_bin("Nonce");
2,353✔
417
         const std::vector<uint8_t> input = vars.get_req_bin("In");
2,353✔
418
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
2,353✔
419
         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
2,353✔
420

421
         Test::Result result(algo);
4,706✔
422

423
         auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption);
2,353✔
424
         auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption);
2,353✔
425

426
         if(!enc || !dec) {
2,353✔
427
            result.note_missing(algo);
×
428
            return result;
429
         }
430

431
         // must be authenticated
432
         result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true);
2,353✔
433
         result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true);
2,353✔
434

435
         const std::string enc_provider = enc->provider();
2,353✔
436
         result.test_is_nonempty("enc provider", enc_provider);
2,353✔
437
         const std::string dec_provider = enc->provider();
2,353✔
438
         result.test_is_nonempty("dec provider", dec_provider);
2,353✔
439

440
         result.test_eq("same provider", enc_provider, dec_provider);
2,353✔
441

442
         // FFI currently requires this, so assure it is true for all modes
443
         result.test_gt("enc buffer sizes ok", enc->ideal_granularity(), enc->minimum_final_size());
2,353✔
444
         result.test_gt("dec buffer sizes ok", dec->ideal_granularity(), dec->minimum_final_size());
2,353✔
445

446
         result.test_gt("update granularity is non-zero", enc->update_granularity(), 0);
2,353✔
447

448
         result.test_eq(
2,353✔
449
            "enc and dec ideal granularity is the same", enc->ideal_granularity(), dec->ideal_granularity());
2,353✔
450

451
         result.test_gt(
2,353✔
452
            "ideal granularity is at least update granularity", enc->ideal_granularity(), enc->update_granularity());
2,353✔
453

454
         result.confirm("ideal granularity is a multiple of update granularity",
4,706✔
455
                        enc->ideal_granularity() % enc->update_granularity() == 0);
2,353✔
456

457
         // test enc
458
         result.merge(test_enc(key, nonce, input, expected, ad, algo, this->rng()));
2,353✔
459

460
         // test dec
461
         // NOLINTNEXTLINE(*-suspicious-call-argument) Yes we are swapping ptext and ctext arguments here
462
         result.merge(test_dec(key, nonce, expected, input, ad, algo, this->rng()));
2,353✔
463

464
         return result;
2,353✔
465
      }
18,384✔
466
};
467

468
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("modes", "aead", AEAD_Tests);
469

470
#endif
471

472
}  // namespace
473

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

© 2026 Coveralls, Inc