• 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

95.16
/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,348✔
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
         const bool is_siv = algo.find("/SIV") != std::string::npos;
2,348✔
31

32
         Test::Result result(algo);
2,348✔
33

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

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

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

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

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

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

49
         if(enc->associated_data_requires_key()) {
2,348✔
50
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
51
                               [&]() { enc->set_associated_data(ad.data(), ad.size()); });
739✔
52
         }
53

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

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

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

66
         enc->set_associated_data(mutate_vec(ad));
2,348✔
67
         enc->start(mutate_vec(nonce));
2,348✔
68
         enc->update(garbage);
2,348✔
69

70
         // reset message specific state
71
         enc->reset();
2,348✔
72

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

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

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

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

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

105
               enc->set_associated_data(ad);
2,181✔
106
               enc->start(nonce);
2,181✔
107

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

124
               // encrypt remaining bytes
125
               block.assign(p, p + input_length);
2,181✔
126
               enc->finish(block);
2,181✔
127
               buffer_insert(ciphertext, 0 + offset, block);
2,181✔
128

129
               result.test_eq("encrypt update", ciphertext, expected);
4,362✔
130
            }
4,362✔
131

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

138
               enc->set_associated_data(ad);
2,181✔
139
               enc->start(nonce);
2,181✔
140

141
               buf.assign(input.begin(), input.end());
2,181✔
142

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

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

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

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

162
               result.test_eq("encrypt process", buf, expected);
4,362✔
163
            }
164
         }
165

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

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

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

178
         return result;
2,348✔
179
      }
7,044✔
180

181
      static Test::Result test_dec(const std::vector<uint8_t>& key,
2,348✔
182
                                   const std::vector<uint8_t>& nonce,
183
                                   const std::vector<uint8_t>& input,
184
                                   const std::vector<uint8_t>& expected,
185
                                   const std::vector<uint8_t>& ad,
186
                                   const std::string& algo) {
187
         const bool is_siv = algo.find("/SIV") != std::string::npos;
2,348✔
188

189
         Test::Result result(algo);
2,348✔
190

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

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

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

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

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

203
         if(dec->associated_data_requires_key()) {
2,348✔
204
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
205
                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
739✔
206
         }
207

208
         // First some tests for reset() to make sure it resets what we need it to
209
         // set garbage values
210
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,348✔
211
         dec->set_key(key);
2,348✔
212
         result.test_eq("key is set", dec->has_keying_material(), true);
2,348✔
213
         dec->set_associated_data(mutate_vec(ad));
2,348✔
214

215
         if(is_siv == false) {
2,348✔
216
            result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->update(garbage); });
6,078✔
217
            result.test_throws("Cannot process data until nonce is set (dec)", [&]() { dec->finish(garbage); });
8,104✔
218
         }
219

220
         dec->start(mutate_vec(nonce));
2,348✔
221

222
         dec->update(garbage);
2,348✔
223

224
         // reset message specific state
225
         dec->reset();
2,348✔
226

227
         Botan::secure_vector<uint8_t> buf(input.begin(), input.end());
2,348✔
228
         try {
2,348✔
229
            // now try to decrypt with correct values
230

231
            try {
2,348✔
232
               dec->start(nonce);
2,348✔
233
               dec->set_associated_data(ad);
2,348✔
234
            } catch(Botan::Invalid_State&) {
1,946✔
235
               // ad after setting nonce rejected, in this case we need to reset
236
               dec->reset();
1,946✔
237
               dec->set_associated_data(ad);
1,946✔
238
               dec->start(nonce);
1,946✔
239
            }
1,946✔
240

241
            // test finish() with full input
242
            dec->finish(buf);
2,348✔
243
            result.test_eq("decrypt full", buf, expected);
2,348✔
244

245
            // additionally test update() if possible
246
            const size_t update_granularity = dec->update_granularity();
2,348✔
247
            if(input.size() > update_granularity) {
2,348✔
248
               // reset state first
249
               dec->reset();
2,321✔
250

251
               dec->set_associated_data(ad);
2,321✔
252
               dec->start(nonce);
2,321✔
253

254
               buf.assign(input.begin(), input.end());
2,321✔
255
               size_t input_length = buf.size();
2,321✔
256
               size_t offset = 0;
2,321✔
257
               uint8_t* p = buf.data();
2,321✔
258
               Botan::secure_vector<uint8_t> block(update_granularity);
2,321✔
259
               Botan::secure_vector<uint8_t> plaintext(dec->output_length(buf.size()));
2,321✔
260
               while((input_length > update_granularity) &&
216,782✔
261
                     ((input_length - update_granularity) >= dec->minimum_final_size())) {
108,353✔
262
                  block.assign(p, p + update_granularity);
106,108✔
263
                  dec->update(block);
106,108✔
264
                  p += update_granularity;
106,108✔
265
                  input_length -= update_granularity;
106,108✔
266
                  buffer_insert(plaintext, 0 + offset, block);
106,108✔
267
                  offset += block.size();
106,108✔
268
               }
269

270
               // decrypt remaining bytes
271
               block.assign(p, p + input_length);
2,321✔
272
               dec->finish(block);
2,321✔
273
               buffer_insert(plaintext, 0 + offset, block);
2,321✔
274

275
               result.test_eq("decrypt update", plaintext, expected);
4,642✔
276
            }
4,642✔
277

278
            // additionally test process() if possible
279
            const size_t min_final_size = dec->minimum_final_size();
2,348✔
280
            if(input.size() > (update_granularity + min_final_size)) {
2,348✔
281
               // again reset state first
282
               dec->reset();
2,181✔
283

284
               dec->set_associated_data(ad);
2,181✔
285
               dec->start(nonce);
2,181✔
286

287
               buf.assign(input.begin(), input.end());
2,181✔
288

289
               // we can process at max input.size()
290
               const size_t max_blocks_to_process = (input.size() - min_final_size) / update_granularity;
2,181✔
291
               const size_t bytes_to_process = max_blocks_to_process * update_granularity;
2,181✔
292

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

295
               result.confirm("Process returns data unless requires_entire_message",
4,362✔
296
                              dec->requires_entire_message(),
2,181✔
297
                              bytes_written == 0);
298

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

308
               result.test_eq("decrypt process", buf, expected);
4,362✔
309
            }
310

311
         } catch(Botan::Exception& e) { result.test_failure("Failure processing AEAD ciphertext", e.what()); }
×
312

313
         // test decryption with modified ciphertext
314
         const std::vector<uint8_t> mutated_input = mutate_vec(input, true);
2,348✔
315
         buf.assign(mutated_input.begin(), mutated_input.end());
2,348✔
316

317
         dec->reset();
2,348✔
318

319
         dec->set_associated_data(ad);
2,348✔
320
         dec->start(nonce);
2,348✔
321

322
         try {
2,348✔
323
            dec->finish(buf);
2,348✔
324
            result.test_failure("accepted modified message", mutated_input);
×
325
         } catch(Botan::Integrity_Failure&) {
2,348✔
326
            result.test_success("correctly rejected modified message");
2,348✔
327
         } catch(std::exception& e) {
2,348✔
328
            result.test_failure("unexpected error while rejecting modified message", e.what());
×
329
         }
×
330

331
         // test decryption with modified nonce
332
         if(!nonce.empty()) {
2,348✔
333
            buf.assign(input.begin(), input.end());
2,022✔
334
            std::vector<uint8_t> bad_nonce = mutate_vec(nonce);
2,022✔
335

336
            dec->reset();
2,022✔
337
            dec->set_associated_data(ad);
2,022✔
338
            dec->start(bad_nonce);
2,022✔
339

340
            try {
2,022✔
341
               dec->finish(buf);
2,022✔
342
               result.test_failure("accepted message with modified nonce", bad_nonce);
×
343
            } catch(Botan::Integrity_Failure&) {
2,022✔
344
               result.test_success("correctly rejected modified nonce");
2,022✔
345
            } catch(std::exception& e) {
2,022✔
346
               result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
347
            }
×
348
         }
2,022✔
349

350
         // test decryption with modified associated_data
351
         const std::vector<uint8_t> bad_ad = mutate_vec(ad, true);
2,348✔
352

353
         dec->reset();
2,348✔
354
         dec->set_associated_data(bad_ad);
2,348✔
355

356
         dec->start(nonce);
2,348✔
357

358
         try {
2,348✔
359
            buf.assign(input.begin(), input.end());
2,348✔
360
            dec->finish(buf);
2,348✔
361
            result.test_failure("accepted message with modified ad", bad_ad);
×
362
         } catch(Botan::Integrity_Failure&) {
2,348✔
363
            result.test_success("correctly rejected modified ad");
2,348✔
364
         } catch(std::exception& e) {
2,348✔
365
            result.test_failure("unexpected error while rejecting modified nonce", e.what());
×
366
         }
×
367

368
         // Make sure we can set the AD after processing a message
369
         dec->set_associated_data(ad);
2,348✔
370
         dec->clear();
2,348✔
371
         result.test_eq("key is not set", dec->has_keying_material(), false);
2,348✔
372

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

375
         if(dec->associated_data_requires_key()) {
2,348✔
376
            result.test_throws("Unkeyed object throws for set AD",
2,217✔
377
                               [&]() { dec->set_associated_data(ad.data(), ad.size()); });
739✔
378
         }
379

380
         return result;
2,348✔
381
      }
11,740✔
382

383
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
2,348✔
384
         const std::vector<uint8_t> key = vars.get_req_bin("Key");
2,348✔
385
         const std::vector<uint8_t> nonce = vars.get_opt_bin("Nonce");
2,348✔
386
         const std::vector<uint8_t> input = vars.get_req_bin("In");
2,348✔
387
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
2,348✔
388
         const std::vector<uint8_t> ad = vars.get_opt_bin("AD");
2,348✔
389

390
         Test::Result result(algo);
4,696✔
391

392
         auto enc = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Encryption);
2,348✔
393
         auto dec = Botan::AEAD_Mode::create(algo, Botan::Cipher_Dir::Decryption);
2,348✔
394

395
         if(!enc || !dec) {
2,348✔
396
            result.note_missing(algo);
×
397
            return result;
398
         }
399

400
         // must be authenticated
401
         result.test_eq("Encryption algo is an authenticated mode", enc->authenticated(), true);
2,348✔
402
         result.test_eq("Decryption algo is an authenticated mode", dec->authenticated(), true);
2,348✔
403

404
         const std::string enc_provider = enc->provider();
2,348✔
405
         result.test_is_nonempty("enc provider", enc_provider);
2,348✔
406
         const std::string dec_provider = enc->provider();
2,348✔
407
         result.test_is_nonempty("dec provider", dec_provider);
2,348✔
408

409
         result.test_eq("same provider", enc_provider, dec_provider);
2,348✔
410

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

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

417
         result.test_eq(
2,348✔
418
            "enc and dec ideal granularity is the same", enc->ideal_granularity(), dec->ideal_granularity());
2,348✔
419

420
         result.test_gt(
2,348✔
421
            "ideal granularity is at least update granularity", enc->ideal_granularity(), enc->update_granularity());
2,348✔
422

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

426
         // test enc
427
         result.merge(test_enc(key, nonce, input, expected, ad, algo));
2,348✔
428

429
         // test dec
430
         result.merge(test_dec(key, nonce, expected, input, ad, algo));
2,348✔
431

432
         return result;
2,348✔
433
      }
20,692✔
434
};
435

436
BOTAN_REGISTER_SERIALIZED_SMOKE_TEST("modes", "aead", AEAD_Tests);
437

438
#endif
439

440
}  // namespace
441

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