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

randombit / botan / 24922258954

24 Apr 2026 12:23PM UTC coverage: 89.387% (-0.01%) from 89.401%
24922258954

push

github

web-flow
Merge pull request #5546 from randombit/jack/crldp-fixes

Fix some bugs relating to CRL distribution point handling

106817 of 119500 relevant lines covered (89.39%)

11464956.38 hits per line

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

87.3
/src/tests/test_asn1.cpp
1
/*
2
* (C) 2017 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_ASN1)
10
   #include <botan/asn1_obj.h>
11
   #include <botan/asn1_print.h>
12
   #include <botan/asn1_time.h>
13
   #include <botan/ber_dec.h>
14
   #include <botan/bigint.h>
15
   #include <botan/data_src.h>
16
   #include <botan/der_enc.h>
17
   #include <botan/internal/fmt.h>
18
#endif
19

20
namespace Botan_Tests {
21

22
namespace {
23

24
#if defined(BOTAN_HAS_ASN1)
25

26
Test::Result test_ber_stack_recursion() {
1✔
27
   Test::Result result("BER stack recursion");
1✔
28

29
   // OSS-Fuzz #813 GitHub #989
30

31
   try {
1✔
32
      const std::vector<uint8_t> in(10000000, 0);
1✔
33
      Botan::DataSource_Memory input(in.data(), in.size());
1✔
34
      Botan::BER_Decoder dec(input);
1✔
35

36
      while(dec.more_items()) {
2✔
37
         Botan::BER_Object obj;
1✔
38
         dec.get_next(obj);
1✔
39
      }
1✔
40
   } catch(Botan::Decoding_Error&) {}
2✔
41

42
   result.test_success("No crash");
1✔
43

44
   return result;
1✔
45
}
×
46

47
Test::Result test_ber_eoc_decoding_limits() {
1✔
48
   Test::Result result("BER nested indefinite length");
1✔
49

50
   // OSS-Fuzz #4353
51

52
   const Botan::ASN1_Pretty_Printer printer;
1✔
53

54
   size_t max_eoc_allowed = 0;
1✔
55

56
   for(size_t len = 1; len < 1024; ++len) {
17✔
57
      std::vector<uint8_t> buf(4 * len);
17✔
58

59
      /*
60
      This constructs a len deep sequence of SEQUENCES each with
61
      an indefinite length
62
      */
63
      for(size_t i = 0; i != 2 * len; i += 2) {
170✔
64
         buf[i] = 0x30;
153✔
65
         buf[i + 1] = 0x80;
153✔
66
      }
67
      // remainder of values left as zeros (EOC markers)
68

69
      try {
17✔
70
         printer.print(buf);
17✔
71
      } catch(Botan::BER_Decoding_Error&) {
1✔
72
         max_eoc_allowed = len - 1;
1✔
73
         break;
1✔
74
      }
1✔
75
   }
17✔
76

77
   result.test_sz_eq("EOC limited to prevent stack exhaustion", max_eoc_allowed, 16);
1✔
78

79
   return result;
1✔
80
}
1✔
81

82
Test::Result test_asn1_utf8_ascii_parsing() {
1✔
83
   Test::Result result("ASN.1 ASCII parsing");
1✔
84

85
   try {
1✔
86
      // \x13 - ASN1 tag for 'printable string'
87
      // \x06 - 6 characters of payload
88
      // ...  - UTF-8 encoded (ASCII chars only) word 'Moscow'
89
      const std::string moscow = "\x13\x06\x4D\x6F\x73\x63\x6F\x77";
1✔
90
      const std::string moscow_plain = "Moscow";
1✔
91
      Botan::DataSource_Memory input(moscow);
1✔
92
      Botan::BER_Decoder dec(input);
1✔
93

94
      Botan::ASN1_String str;
1✔
95
      str.decode_from(dec);
1✔
96

97
      result.test_str_eq("value()", str.value(), moscow_plain);
1✔
98
   } catch(const Botan::Decoding_Error& ex) {
2✔
99
      result.test_failure(ex.what());
×
100
   }
×
101

102
   return result;
1✔
103
}
×
104

105
Test::Result test_asn1_utf8_parsing() {
1✔
106
   Test::Result result("ASN.1 UTF-8 parsing");
1✔
107

108
   try {
1✔
109
      // \x0C - ASN1 tag for 'UTF8 string'
110
      // \x0C - 12 characters of payload
111
      // ...  - UTF-8 encoded russian word for Moscow in cyrillic script
112
      const std::string moscow = "\x0C\x0C\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
113
      const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
114
      Botan::DataSource_Memory input(moscow);
1✔
115
      Botan::BER_Decoder dec(input);
1✔
116

117
      Botan::ASN1_String str;
1✔
118
      str.decode_from(dec);
1✔
119

120
      result.test_str_eq("value()", str.value(), moscow_plain);
1✔
121
   } catch(const Botan::Decoding_Error& ex) {
2✔
122
      result.test_failure(ex.what());
×
123
   }
×
124

125
   return result;
1✔
126
}
×
127

128
Test::Result test_asn1_ucs2_parsing() {
1✔
129
   Test::Result result("ASN.1 BMP string (UCS-2) parsing");
1✔
130

131
   try {
1✔
132
      // \x1E     - ASN1 tag for 'BMP (UCS-2) string'
133
      // \x0C     - 12 characters of payload
134
      // ...      - UCS-2 encoding for Moscow in cyrillic script
135
      const std::string moscow = "\x1E\x0C\x04\x1C\x04\x3E\x04\x41\x04\x3A\x04\x32\x04\x30";
1✔
136
      const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
137

138
      Botan::DataSource_Memory input(moscow);
1✔
139
      Botan::BER_Decoder dec(input);
1✔
140

141
      Botan::ASN1_String str;
1✔
142
      str.decode_from(dec);
1✔
143

144
      result.test_str_eq("value()", str.value(), moscow_plain);
1✔
145
   } catch(const Botan::Decoding_Error& ex) {
2✔
146
      result.test_failure(ex.what());
×
147
   }
×
148

149
   return result;
1✔
150
}
×
151

152
Test::Result test_asn1_ucs4_parsing() {
1✔
153
   Test::Result result("ASN.1 universal string (UCS-4) parsing");
1✔
154

155
   try {
1✔
156
      // \x1C - ASN1 tag for 'universal string'
157
      // \x18 - 24 characters of payload
158
      // ...  - UCS-4 encoding for Moscow in cyrillic script
159
      const uint8_t moscow[] =
1✔
160
         "\x1C\x18\x00\x00\x04\x1C\x00\x00\x04\x3E\x00\x00\x04\x41\x00\x00\x04\x3A\x00\x00\x04\x32\x00\x00\x04\x30";
161
      const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
162
      Botan::DataSource_Memory input(moscow, sizeof(moscow));
1✔
163
      Botan::BER_Decoder dec(input);
1✔
164

165
      Botan::ASN1_String str;
1✔
166
      str.decode_from(dec);
1✔
167

168
      result.test_str_eq("value()", str.value(), moscow_plain);
1✔
169
   } catch(const Botan::Decoding_Error& ex) {
2✔
170
      result.test_failure(ex.what());
×
171
   }
×
172

173
   return result;
1✔
174
}
×
175

176
Test::Result test_asn1_ascii_encoding() {
1✔
177
   Test::Result result("ASN.1 ASCII encoding");
1✔
178

179
   try {
1✔
180
      // UTF-8 encoded (ASCII chars only) word 'Moscow'
181
      const std::string moscow = "Moscow";
1✔
182
      const Botan::ASN1_String str(moscow);
1✔
183

184
      Botan::DER_Encoder enc;
1✔
185

186
      str.encode_into(enc);
1✔
187
      auto encodingResult = enc.get_contents();
1✔
188

189
      // \x13 - ASN1 tag for 'printable string'
190
      // \x06 - 6 characters of payload
191
      result.test_bin_eq("encoding result", encodingResult, "13064D6F73636F77");
1✔
192

193
      result.test_success("No crash");
1✔
194
   } catch(const std::exception& ex) {
2✔
195
      result.test_failure(ex.what());
×
196
   }
×
197

198
   return result;
1✔
199
}
×
200

201
Test::Result test_asn1_utf8_encoding() {
1✔
202
   Test::Result result("ASN.1 UTF-8 encoding");
1✔
203

204
   try {
1✔
205
      // UTF-8 encoded russian word for Moscow in cyrillic script
206
      const std::string moscow = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
207
      const Botan::ASN1_String str(moscow);
1✔
208

209
      Botan::DER_Encoder enc;
1✔
210

211
      str.encode_into(enc);
1✔
212
      auto encodingResult = enc.get_contents();
1✔
213

214
      // \x0C - ASN1 tag for 'UTF8 string'
215
      // \x0C - 12 characters of payload
216
      result.test_bin_eq("encoding result", encodingResult, "0C0CD09CD0BED181D0BAD0B2D0B0");
1✔
217

218
      result.test_success("No crash");
1✔
219
   } catch(const std::exception& ex) {
2✔
220
      result.test_failure(ex.what());
×
221
   }
×
222

223
   return result;
1✔
224
}
×
225

226
Test::Result test_asn1_tag_underlying_type() {
1✔
227
   Test::Result result("ASN.1 class and type underlying type");
1✔
228

229
   if constexpr(std::is_same_v<std::underlying_type_t<Botan::ASN1_Class>, std::underlying_type_t<Botan::ASN1_Type>>) {
1✔
230
      if constexpr(!std::is_same_v<std::underlying_type_t<Botan::ASN1_Class>,
1✔
231
                                   std::invoke_result_t<decltype(&Botan::BER_Object::tagging), Botan::BER_Object>>) {
232
         result.test_failure(
233
            "Return type of BER_Object::tagging() is different than the underlying type of ASN1_Class");
234
      } else {
235
         result.test_success("Same types");
1✔
236
      }
237
   } else {
238
      result.test_failure("ASN1_Class and ASN1_Type have different underlying types");
239
   }
240

241
   return result;
1✔
242
}
×
243

244
Test::Result test_asn1_negative_int_encoding() {
1✔
245
   Test::Result result("DER encode/decode of negative integers");
1✔
246

247
   BigInt n(32);
1✔
248

249
   for(size_t i = 0; i != 2048; ++i) {
2,049✔
250
      n--;
2,048✔
251

252
      const auto enc = Botan::DER_Encoder().encode(n).get_contents_unlocked();
2,048✔
253

254
      BigInt n_dec;
2,048✔
255
      Botan::BER_Decoder(enc, Botan::BER_Decoder::Limits::DER()).decode(n_dec);
4,096✔
256

257
      result.test_bn_eq("DER encoding round trips negative integers", n_dec, n);
2,048✔
258
   }
2,048✔
259

260
   return result;
1✔
261
}
1✔
262

263
Test::Result test_ber_indefinite_length_trailing_data() {
1✔
264
   Test::Result result("BER indefinite length trailing data");
1✔
265

266
   // Case 1: verify_end after consuming indef SEQUENCE
267
   try {
1✔
268
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00};
1✔
269
      Botan::BER_Decoder dec(enc);
1✔
270
      Botan::BigInt x;
1✔
271
      dec.start_sequence().decode(x).end_cons();
1✔
272
      dec.verify_end();
1✔
273
      result.test_bn_eq("verify_end decoded x", x, Botan::BigInt(0x42));
1✔
274
   } catch(Botan::Exception& e) {
1✔
275
      result.test_failure("verify_end after indef SEQUENCE", e.what());
×
276
   }
×
277

278
   // Case 2: two back-to-back indef SEQUENCES at top level
279
   try {
1✔
280
      const std::vector<uint8_t> enc = {
1✔
281
         0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x43, 0x00, 0x00};
1✔
282
      Botan::BER_Decoder dec(enc);
1✔
283
      Botan::BigInt x;
1✔
284
      Botan::BigInt y;
1✔
285
      dec.start_sequence().decode(x).end_cons();
1✔
286
      dec.start_sequence().decode(y).end_cons();
1✔
287
      dec.verify_end();
1✔
288
      result.test_bn_eq("back-to-back x", x, Botan::BigInt(0x42));
1✔
289
      result.test_bn_eq("back-to-back y", y, Botan::BigInt(0x43));
1✔
290
   } catch(Botan::Exception& e) {
1✔
291
      result.test_failure("two back-to-back indef SEQUENCES", e.what());
×
292
   }
×
293

294
   // Case 3: nested indef SEQUENCES
295
   try {
1✔
296
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00};
1✔
297
      Botan::BER_Decoder dec(enc);
1✔
298
      Botan::BigInt x;
1✔
299
      auto outer = dec.start_sequence();
1✔
300
      outer.start_sequence().decode(x).end_cons();
1✔
301
      outer.end_cons();
1✔
302
      dec.verify_end();
1✔
303
      result.test_bn_eq("nested x", x, Botan::BigInt(0x42));
1✔
304
   } catch(Botan::Exception& e) {
1✔
305
      result.test_failure("nested indef SEQUENCE", e.what());
×
306
   }
×
307

308
   // Case 4: while(more_items()) loop over an indef SEQUENCE
309
   try {
1✔
310
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x02, 0x01, 0x43, 0x00, 0x00};
1✔
311
      Botan::BER_Decoder dec(enc);
1✔
312
      auto seq = dec.start_sequence();
1✔
313
      std::vector<Botan::BigInt> xs;
1✔
314
      while(seq.more_items()) {
3✔
315
         Botan::BigInt x;
2✔
316
         seq.decode(x);
2✔
317
         xs.push_back(x);
2✔
318
      }
2✔
319
      seq.end_cons();
1✔
320
      dec.verify_end();
1✔
321
      result.test_sz_eq("more_items count", xs.size(), 2);
1✔
322
      if(xs.size() == 2) {
1✔
323
         result.test_bn_eq("more_items xs[0]", xs[0], Botan::BigInt(0x42));
1✔
324
         result.test_bn_eq("more_items xs[1]", xs[1], Botan::BigInt(0x43));
1✔
325
      }
326
   } catch(Botan::Exception& e) {
1✔
327
      result.test_failure("more_items loop over indef SEQUENCE", e.what());
×
328
   }
×
329

330
   return result;
1✔
331
}
×
332

333
Test::Result test_ber_find_eoc() {
1✔
334
   Test::Result result("BER indefinite length EOC matching");
1✔
335

336
   const size_t num_siblings = 4096;
1✔
337

338
   std::vector<uint8_t> ber;
1✔
339
   ber.push_back(0x30);  // outer SEQUENCE | CONSTRUCTED
1✔
340
   ber.push_back(0x80);  // indefinite length
1✔
341
   for(size_t i = 0; i != num_siblings; ++i) {
4,097✔
342
      ber.push_back(0x30);  // inner SEQUENCE | CONSTRUCTED
4,096✔
343
      ber.push_back(0x80);  // indefinite length
4,096✔
344
      ber.push_back(0x00);  // EOC tag
4,096✔
345
      ber.push_back(0x00);  // EOC length
4,096✔
346
   }
347
   ber.push_back(0x00);  // outer EOC tag
1✔
348
   ber.push_back(0x00);  // outer EOC length
1✔
349

350
   try {
1✔
351
      Botan::BER_Decoder dec(ber);
1✔
352
      const Botan::BER_Object obj = dec.get_next_object();
1✔
353

354
      result.test_sz_eq("object body includes children", obj.length(), num_siblings * 4);
1✔
355
   } catch(Botan::Exception& e) {
1✔
356
      result.test_failure("decode failed", e.what());
×
357
   }
×
358

359
   return result;
1✔
360
}
1✔
361

362
class ASN1_Tests final : public Test {
1✔
363
   public:
364
      std::vector<Test::Result> run() override {
1✔
365
         std::vector<Test::Result> results;
1✔
366

367
         results.push_back(test_ber_stack_recursion());
2✔
368
         results.push_back(test_ber_eoc_decoding_limits());
2✔
369
         results.push_back(test_ber_indefinite_length_trailing_data());
2✔
370
         results.push_back(test_ber_find_eoc());
2✔
371
         results.push_back(test_asn1_utf8_ascii_parsing());
2✔
372
         results.push_back(test_asn1_utf8_parsing());
2✔
373
         results.push_back(test_asn1_ucs2_parsing());
2✔
374
         results.push_back(test_asn1_ucs4_parsing());
2✔
375
         results.push_back(test_asn1_ascii_encoding());
2✔
376
         results.push_back(test_asn1_utf8_encoding());
2✔
377
         results.push_back(test_asn1_tag_underlying_type());
2✔
378
         results.push_back(test_asn1_negative_int_encoding());
2✔
379

380
         return results;
1✔
381
      }
×
382
};
383

384
BOTAN_REGISTER_TEST("asn1", "asn1_encoding", ASN1_Tests);
385

386
class ASN1_Time_Parsing_Tests final : public Text_Based_Test {
×
387
   public:
388
      ASN1_Time_Parsing_Tests() : Text_Based_Test("asn1_time.vec", "Tspec") {}
2✔
389

390
      Test::Result run_one_test(const std::string& tag_str, const VarMap& vars) override {
25✔
391
         Test::Result result("ASN.1 date parsing");
25✔
392

393
         const std::string tspec = vars.get_req_str("Tspec");
25✔
394

395
         if(tag_str != "UTC" && tag_str != "UTC.invalid" && tag_str != "Generalized" &&
25✔
396
            tag_str != "Generalized.invalid") {
11✔
397
            throw Test_Error("Invalid tag value in ASN1 date parsing test");
×
398
         }
399

400
         const Botan::ASN1_Type tag = (tag_str == "UTC" || tag_str == "UTC.invalid")
24✔
401
                                         ? Botan::ASN1_Type::UtcTime
25✔
402
                                         : Botan::ASN1_Type::GeneralizedTime;
25✔
403

404
         const bool valid = tag_str.find(".invalid") == std::string::npos;
25✔
405

406
         if(valid) {
25✔
407
            const Botan::ASN1_Time time(tspec, tag);
13✔
408
            result.test_success("Accepted valid time");
13✔
409
         } else {
13✔
410
            result.test_throws("Invalid time rejected", [=]() { const Botan::ASN1_Time time(tspec, tag); });
60✔
411
         }
412

413
         return result;
25✔
414
      }
25✔
415
};
416

417
BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests);
418

419
class ASN1_Printer_Tests final : public Test {
1✔
420
   public:
421
      std::vector<Test::Result> run() override {
1✔
422
         Test::Result result("ASN1_Pretty_Printer");
1✔
423

424
         const Botan::ASN1_Pretty_Printer printer;
1✔
425

426
         const size_t num_tests = 7;
1✔
427

428
         for(size_t i = 1; i <= num_tests; ++i) {
8✔
429
            const std::string i_str = std::to_string(i);
7✔
430
            const std::vector<uint8_t> input_data = Test::read_binary_data_file("asn1_print/input" + i_str + ".der");
21✔
431
            const std::string expected_output = Test::read_data_file("asn1_print/output" + i_str + ".txt");
21✔
432

433
            try {
7✔
434
               const std::string output = printer.print(input_data);
7✔
435
               result.test_str_eq("Test " + i_str, output, expected_output);
14✔
436
            } catch(Botan::Exception& e) {
7✔
437
               result.test_failure(Botan::fmt("Printing test {} failed with an exception: '{}'", i, e.what()));
×
438
            }
×
439
         }
7✔
440

441
         return {result};
2✔
442
      }
9✔
443
};
444

445
BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests);
446

447
class ASN1_Decoding_Tests final : public Text_Based_Test {
×
448
   public:
449
      ASN1_Decoding_Tests() : Text_Based_Test("asn1_decoding.vec", "Input,ResultBER", "ResultDER") {}
2✔
450

451
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
55✔
452
         const auto input = vars.get_req_bin("Input");
55✔
453
         const std::string expected_ber = vars.get_req_str("ResultBER");
55✔
454
         const std::string expected_der = vars.get_opt_str("ResultDER", expected_ber);
55✔
455

456
         Test::Result result("ASN1 decoding");
55✔
457

458
         decoding_test(result, input, expected_ber, false);
55✔
459
         decoding_test(result, input, expected_der, true);
55✔
460

461
         return result;
110✔
462
      }
55✔
463

464
   private:
465
      static void decoding_test(Test::Result& result,
110✔
466
                                std::span<const uint8_t> input,
467
                                std::string_view expected,
468
                                bool require_der) {
469
         const Botan::ASN1_Pretty_Printer printer(4096, 2048, true, 0, 60, 64, require_der);
110✔
470
         const std::string mode = require_der ? "DER" : "BER";
165✔
471
         std::ostringstream sink;
110✔
472

473
         try {
110✔
474
            printer.print_to_stream(sink, input.data(), input.size());
110✔
475

476
            if(expected == "OK") {
52✔
477
               result.test_success();
52✔
478
            } else {
479
               result.test_failure(Botan::fmt("Accepted invalid {} input, expected error {}", mode, expected));
×
480
            }
481
         } catch(const std::exception& e) {
58✔
482
            if(expected == "OK") {
58✔
483
               result.test_failure(Botan::fmt("Rejected valid {} input with {}", mode, e.what()));
×
484
            } else {
485
               // BER_Decoding_Error prepends "BER: " to the message
486
               std::string msg = e.what();
58✔
487
               if(msg.starts_with("BER: ")) {
58✔
488
                  msg = msg.substr(5);
38✔
489
               }
490
               result.test_str_eq("error message", msg, expected);
58✔
491
            }
58✔
492
         }
58✔
493
      }
148✔
494
};
495

496
BOTAN_REGISTER_TEST("asn1", "asn1_decoding", ASN1_Decoding_Tests);
497

498
#endif
499

500
}  // namespace
501

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