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

randombit / botan / 25650639339

10 May 2026 07:03PM UTC coverage: 89.326% (-0.002%) from 89.328%
25650639339

push

github

web-flow
Merge pull request #5592 from randombit/jack/bn-hardening

Various BigInt/mp related hardenings and bug fixes

107853 of 120741 relevant lines covered (89.33%)

11294230.95 hits per line

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

89.1
/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/pss_params.h>
18
   #include <botan/internal/fmt.h>
19
#endif
20

21
namespace Botan_Tests {
22

23
namespace {
24

25
#if defined(BOTAN_HAS_ASN1)
26

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

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

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

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

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

45
   return result;
1✔
46
}
×
47

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

51
   // OSS-Fuzz #4353
52

53
   const Botan::ASN1_Pretty_Printer printer;
1✔
54

55
   size_t max_eoc_allowed = 0;
1✔
56

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

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

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

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

80
   return result;
1✔
81
}
1✔
82

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

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

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

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

103
   return result;
1✔
104
}
×
105

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

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

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

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

126
   return result;
1✔
127
}
×
128

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

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

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

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

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

150
   return result;
1✔
151
}
×
152

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

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

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

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

174
   return result;
1✔
175
}
×
176

177
Test::Result test_asn1_ucs_invalid_codepoint_rejection() {
1✔
178
   Test::Result result("ASN.1 UCS-2/UCS-4 invalid codepoint rejection");
1✔
179

180
   auto expect_decode_throws = [&](const char* what, const std::vector<uint8_t>& wire) {
7✔
181
      result.test_throws(what, [&]() {
6✔
182
         Botan::DataSource_Memory input(wire.data(), wire.size());
6✔
183
         Botan::BER_Decoder dec(input);
6✔
184
         Botan::ASN1_String str;
6✔
185
         str.decode_from(dec);
6✔
186
      });
6✔
187
   };
6✔
188

189
   auto expect_decode_ok = [&](const char* what, const std::vector<uint8_t>& wire) {
2✔
190
      try {
1✔
191
         Botan::DataSource_Memory input(wire.data(), wire.size());
1✔
192
         Botan::BER_Decoder dec(input);
1✔
193
         Botan::ASN1_String str;
1✔
194
         str.decode_from(dec);
1✔
195
         result.test_success(what);
1✔
196
      } catch(const std::exception& ex) {
2✔
197
         result.test_failure(Botan::fmt("{}: unexpected throw: {}", what, ex.what()));
×
198
      }
×
199
   };
1✔
200

201
   // UniversalString (tag 0x1C) with codepoint 0x00110000 - one past Unicode max
202
   expect_decode_throws("UniversalString rejects codepoint > 0x10FFFF", {0x1C, 0x04, 0x00, 0x11, 0x00, 0x00});
1✔
203

204
   // UniversalString with codepoint 0xFFFFFFFF (clearly out of range)
205
   expect_decode_throws("UniversalString rejects codepoint 0xFFFFFFFF", {0x1C, 0x04, 0xFF, 0xFF, 0xFF, 0xFF});
1✔
206

207
   // UniversalString with high surrogate 0xD800
208
   expect_decode_throws("UniversalString rejects surrogate codepoint", {0x1C, 0x04, 0x00, 0x00, 0xD8, 0x00});
1✔
209

210
   // UniversalString boundary case: 0x10FFFF is the highest valid codepoint
211
   expect_decode_ok("UniversalString accepts codepoint 0x10FFFF", {0x1C, 0x04, 0x00, 0x10, 0xFF, 0xFF});
1✔
212

213
   // BmpString (tag 0x1E) with high surrogate
214
   expect_decode_throws("BmpString rejects surrogate codepoint", {0x1E, 0x02, 0xD8, 0x00});
1✔
215

216
   // BmpString with odd length is malformed
217
   expect_decode_throws("BmpString rejects odd-length payload", {0x1E, 0x03, 0x00, 0x41, 0x00});
1✔
218

219
   // UniversalString with non-multiple-of-4 length is malformed
220
   expect_decode_throws("UniversalString rejects non-multiple-of-4 payload",
1✔
221
                        {0x1C, 0x05, 0x00, 0x00, 0x00, 0x41, 0x00});
222

223
   return result;
1✔
224
}
×
225

226
Test::Result test_asn1_ascii_encoding() {
1✔
227
   Test::Result result("ASN.1 ASCII encoding");
1✔
228

229
   try {
1✔
230
      // UTF-8 encoded (ASCII chars only) word 'Moscow'
231
      const std::string moscow = "Moscow";
1✔
232
      const Botan::ASN1_String str(moscow);
1✔
233

234
      Botan::DER_Encoder enc;
1✔
235

236
      str.encode_into(enc);
1✔
237
      auto encodingResult = enc.get_contents();
1✔
238

239
      // \x13 - ASN1 tag for 'printable string'
240
      // \x06 - 6 characters of payload
241
      result.test_bin_eq("encoding result", encodingResult, "13064D6F73636F77");
1✔
242

243
      result.test_success("No crash");
1✔
244
   } catch(const std::exception& ex) {
2✔
245
      result.test_failure(ex.what());
×
246
   }
×
247

248
   return result;
1✔
249
}
×
250

251
Test::Result test_asn1_utf8_encoding() {
1✔
252
   Test::Result result("ASN.1 UTF-8 encoding");
1✔
253

254
   try {
1✔
255
      // UTF-8 encoded russian word for Moscow in cyrillic script
256
      const std::string moscow = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0";
1✔
257
      const Botan::ASN1_String str(moscow);
1✔
258

259
      Botan::DER_Encoder enc;
1✔
260

261
      str.encode_into(enc);
1✔
262
      auto encodingResult = enc.get_contents();
1✔
263

264
      // \x0C - ASN1 tag for 'UTF8 string'
265
      // \x0C - 12 characters of payload
266
      result.test_bin_eq("encoding result", encodingResult, "0C0CD09CD0BED181D0BAD0B2D0B0");
1✔
267

268
      result.test_success("No crash");
1✔
269
   } catch(const std::exception& ex) {
2✔
270
      result.test_failure(ex.what());
×
271
   }
×
272

273
   return result;
1✔
274
}
×
275

276
Test::Result test_asn1_tag_underlying_type() {
1✔
277
   Test::Result result("ASN.1 class and type underlying type");
1✔
278

279
   if constexpr(std::is_same_v<std::underlying_type_t<Botan::ASN1_Class>, std::underlying_type_t<Botan::ASN1_Type>>) {
1✔
280
      if constexpr(!std::is_same_v<std::underlying_type_t<Botan::ASN1_Class>,
1✔
281
                                   std::invoke_result_t<decltype(&Botan::BER_Object::tagging), Botan::BER_Object>>) {
282
         result.test_failure(
283
            "Return type of BER_Object::tagging() is different than the underlying type of ASN1_Class");
284
      } else {
285
         result.test_success("Same types");
1✔
286
      }
287
   } else {
288
      result.test_failure("ASN1_Class and ASN1_Type have different underlying types");
289
   }
290

291
   return result;
1✔
292
}
×
293

294
Test::Result test_asn1_negative_int_encoding() {
1✔
295
   Test::Result result("DER encode/decode of negative integers");
1✔
296

297
   BigInt n(32);
1✔
298

299
   for(size_t i = 0; i != 2048; ++i) {
2,049✔
300
      n--;
2,048✔
301

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

304
      BigInt n_dec;
2,048✔
305
      Botan::BER_Decoder(enc, Botan::BER_Decoder::Limits::DER()).decode(n_dec);
4,096✔
306

307
      result.test_bn_eq("DER encoding round trips negative integers", n_dec, n);
2,048✔
308
   }
2,048✔
309

310
   return result;
1✔
311
}
1✔
312

313
Test::Result test_der_constructed_tag_17_not_sorted() {
1✔
314
   Test::Result result("DER constructed [17] is not SET-sorted");
1✔
315

316
   // Two INTEGERs in descending order. A universal SET would lex-sort and put
317
   // 0x01 before 0x02; a non-universal constructed [17] must preserve order.
318
   const std::vector<uint8_t> first = {0x02, 0x01, 0x02};   // INTEGER 2
1✔
319
   const std::vector<uint8_t> second = {0x02, 0x01, 0x01};  // INTEGER 1
1✔
320

321
   auto encode_with = [&](auto starter) {
5✔
322
      Botan::DER_Encoder enc;
4✔
323
      starter(enc).raw_bytes(first).raw_bytes(second).end_cons();
4✔
324
      return enc.get_contents_unlocked();
8✔
325
   };
5✔
326

327
   // Reference: a universal SET of the same children gets sorted
328
   const auto set_enc = encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_set(); });
2✔
329
   const std::vector<uint8_t> set_expected = {0x31, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02};
1✔
330
   result.test_bin_eq("universal SET is lex-sorted", set_enc, set_expected);
1✔
331

332
   // start_context_specific(17): tag byte = ContextSpecific | Constructed | 17 = 0xB1
333
   const auto ctx_enc =
1✔
334
      encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_context_specific(17); });
2✔
335
   const std::vector<uint8_t> ctx_expected = {0xB1, 0x06, 0x02, 0x01, 0x02, 0x02, 0x01, 0x01};
1✔
336
   result.test_bin_eq("context-specific [17] preserves order", ctx_enc, ctx_expected);
1✔
337

338
   // start_explicit_context_specific(17): same tag byte 0xB1
339
   const auto explicit_ctx_enc =
1✔
340
      encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_explicit_context_specific(17); });
2✔
341
   result.test_bin_eq("explicit context-specific [17] preserves order", explicit_ctx_enc, ctx_expected);
1✔
342

343
   // start_explicit(17): used to throw Internal_Error; must now produce [17] in order
344
   const auto explicit_enc =
1✔
345
      encode_with([](Botan::DER_Encoder& e) -> Botan::DER_Encoder& { return e.start_explicit(17); });
2✔
346
   result.test_bin_eq("start_explicit(17) preserves order", explicit_enc, ctx_expected);
1✔
347

348
   return result;
1✔
349
}
1✔
350

351
Test::Result test_ber_indefinite_length_trailing_data() {
1✔
352
   Test::Result result("BER indefinite length trailing data");
1✔
353

354
   // Case 1: verify_end after consuming indef SEQUENCE
355
   try {
1✔
356
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00};
1✔
357
      Botan::BER_Decoder dec(enc);
1✔
358
      Botan::BigInt x;
1✔
359
      dec.start_sequence().decode(x).end_cons();
1✔
360
      dec.verify_end();
1✔
361
      result.test_bn_eq("verify_end decoded x", x, Botan::BigInt(0x42));
1✔
362
   } catch(Botan::Exception& e) {
1✔
363
      result.test_failure("verify_end after indef SEQUENCE", e.what());
×
364
   }
×
365

366
   // Case 2: two back-to-back indef SEQUENCES at top level
367
   try {
1✔
368
      const std::vector<uint8_t> enc = {
1✔
369
         0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x30, 0x80, 0x02, 0x01, 0x43, 0x00, 0x00};
1✔
370
      Botan::BER_Decoder dec(enc);
1✔
371
      Botan::BigInt x;
1✔
372
      Botan::BigInt y;
1✔
373
      dec.start_sequence().decode(x).end_cons();
1✔
374
      dec.start_sequence().decode(y).end_cons();
1✔
375
      dec.verify_end();
1✔
376
      result.test_bn_eq("back-to-back x", x, Botan::BigInt(0x42));
1✔
377
      result.test_bn_eq("back-to-back y", y, Botan::BigInt(0x43));
1✔
378
   } catch(Botan::Exception& e) {
1✔
379
      result.test_failure("two back-to-back indef SEQUENCES", e.what());
×
380
   }
×
381

382
   // Case 3: nested indef SEQUENCES
383
   try {
1✔
384
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x30, 0x80, 0x02, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00};
1✔
385
      Botan::BER_Decoder dec(enc);
1✔
386
      Botan::BigInt x;
1✔
387
      auto outer = dec.start_sequence();
1✔
388
      outer.start_sequence().decode(x).end_cons();
1✔
389
      outer.end_cons();
1✔
390
      dec.verify_end();
1✔
391
      result.test_bn_eq("nested x", x, Botan::BigInt(0x42));
1✔
392
   } catch(Botan::Exception& e) {
1✔
393
      result.test_failure("nested indef SEQUENCE", e.what());
×
394
   }
×
395

396
   // Case 4: while(more_items()) loop over an indef SEQUENCE
397
   try {
1✔
398
      const std::vector<uint8_t> enc = {0x30, 0x80, 0x02, 0x01, 0x42, 0x02, 0x01, 0x43, 0x00, 0x00};
1✔
399
      Botan::BER_Decoder dec(enc);
1✔
400
      auto seq = dec.start_sequence();
1✔
401
      std::vector<Botan::BigInt> xs;
1✔
402
      while(seq.more_items()) {
3✔
403
         Botan::BigInt x;
2✔
404
         seq.decode(x);
2✔
405
         xs.push_back(x);
2✔
406
      }
2✔
407
      seq.end_cons();
1✔
408
      dec.verify_end();
1✔
409
      result.test_sz_eq("more_items count", xs.size(), 2);
1✔
410
      if(xs.size() == 2) {
1✔
411
         result.test_bn_eq("more_items xs[0]", xs[0], Botan::BigInt(0x42));
1✔
412
         result.test_bn_eq("more_items xs[1]", xs[1], Botan::BigInt(0x43));
1✔
413
      }
414
   } catch(Botan::Exception& e) {
1✔
415
      result.test_failure("more_items loop over indef SEQUENCE", e.what());
×
416
   }
×
417

418
   return result;
1✔
419
}
×
420

421
Test::Result test_ber_find_eoc() {
1✔
422
   Test::Result result("BER indefinite length EOC matching");
1✔
423

424
   const size_t num_siblings = 4096;
1✔
425

426
   std::vector<uint8_t> ber;
1✔
427
   ber.push_back(0x30);  // outer SEQUENCE | CONSTRUCTED
1✔
428
   ber.push_back(0x80);  // indefinite length
1✔
429
   for(size_t i = 0; i != num_siblings; ++i) {
4,097✔
430
      ber.push_back(0x30);  // inner SEQUENCE | CONSTRUCTED
4,096✔
431
      ber.push_back(0x80);  // indefinite length
4,096✔
432
      ber.push_back(0x00);  // EOC tag
4,096✔
433
      ber.push_back(0x00);  // EOC length
4,096✔
434
   }
435
   ber.push_back(0x00);  // outer EOC tag
1✔
436
   ber.push_back(0x00);  // outer EOC length
1✔
437

438
   try {
1✔
439
      Botan::BER_Decoder dec(ber);
1✔
440
      const Botan::BER_Object obj = dec.get_next_object();
1✔
441

442
      result.test_sz_eq("object body includes children", obj.length(), num_siblings * 4);
1✔
443
   } catch(Botan::Exception& e) {
1✔
444
      result.test_failure("decode failed", e.what());
×
445
   }
×
446

447
   return result;
1✔
448
}
1✔
449

450
Test::Result test_asn1_string_zero_length_roundtrip() {
1✔
451
   Test::Result result("ASN.1 String zero-length round-trip");
1✔
452

453
   auto roundtrip = [&](const char* what, const std::vector<uint8_t>& wire) {
4✔
454
      try {
3✔
455
         Botan::DataSource_Memory input(wire.data(), wire.size());
3✔
456
         Botan::BER_Decoder dec(input);
3✔
457
         Botan::ASN1_String str;
3✔
458
         str.decode_from(dec);
3✔
459

460
         Botan::DER_Encoder enc;
3✔
461
         str.encode_into(enc);
3✔
462
         const auto out = enc.get_contents();
3✔
463
         result.test_bin_eq(what, std::span{out}, std::span{wire});
3✔
464
      } catch(const std::exception& ex) {
9✔
465
         result.test_failure(Botan::fmt("{}: unexpected throw: {}", what, ex.what()));
×
466
      }
×
467
   };
3✔
468

469
   roundtrip("BmpString 1E 00", {0x1E, 0x00});
1✔
470
   roundtrip("UniversalString 1C 00", {0x1C, 0x00});
1✔
471
   roundtrip("TeletexString 14 00", {0x14, 0x00});
1✔
472

473
   return result;
1✔
474
}
×
475

476
Test::Result test_pss_params_rejects_trailing_data_in_mgf1_params() {
1✔
477
   Test::Result result("PSS-Params rejects trailing data in MGF1 parameters");
1✔
478

479
   const Botan::AlgorithmIdentifier sha256_alg_id("SHA-256", Botan::AlgorithmIdentifier::USE_NULL_PARAM);
1✔
480
   const auto sha256_der = sha256_alg_id.BER_encode();
1✔
481

482
   auto encode_pss_params = [&](const std::vector<uint8_t>& mgf_params) {
3✔
483
      const Botan::AlgorithmIdentifier mgf("MGF1", mgf_params);
2✔
484
      Botan::DER_Encoder enc;
2✔
485
      enc.start_sequence()
2✔
486
         .start_context_specific(0)
2✔
487
         .encode(sha256_alg_id)
2✔
488
         .end_cons()
2✔
489
         .start_context_specific(1)
2✔
490
         .encode(mgf)
2✔
491
         .end_cons()
2✔
492
         .start_context_specific(2)
2✔
493
         .encode(static_cast<size_t>(32))
2✔
494
         .end_cons()
2✔
495
         .end_cons();
2✔
496
      return enc.get_contents();
2✔
497
   };
4✔
498

499
   try {
1✔
500
      const auto clean_der = encode_pss_params(sha256_der);
1✔
501
      const Botan::PSS_Params clean(clean_der);
1✔
502
      result.test_success("control: clean PSS-Params decodes");
1✔
503
   } catch(const std::exception& e) {
2✔
504
      result.test_failure(Botan::fmt("clean PSS-Params unexpected throw: {}", e.what()));
×
505
   }
×
506

507
   std::vector<uint8_t> mgf_params_with_junk = sha256_der;
1✔
508
   const std::vector<uint8_t> trailing_junk{0x02, 0x01, 0x00};
1✔
509
   mgf_params_with_junk.insert(mgf_params_with_junk.end(), trailing_junk.begin(), trailing_junk.end());
1✔
510
   const auto bad_der = encode_pss_params(mgf_params_with_junk);
1✔
511

512
   result.test_throws<Botan::Decoding_Error>("PSS-Params rejects trailing data in MGF1 parameters",
1✔
513
                                             [&]() { const Botan::PSS_Params bad(bad_der); });
2✔
514

515
   return result;
1✔
516
}
2✔
517

518
class ASN1_Tests final : public Test {
1✔
519
   public:
520
      std::vector<Test::Result> run() override {
1✔
521
         std::vector<Test::Result> results;
1✔
522

523
         results.push_back(test_ber_stack_recursion());
2✔
524
         results.push_back(test_ber_eoc_decoding_limits());
2✔
525
         results.push_back(test_ber_indefinite_length_trailing_data());
2✔
526
         results.push_back(test_ber_find_eoc());
2✔
527
         results.push_back(test_asn1_utf8_ascii_parsing());
2✔
528
         results.push_back(test_asn1_utf8_parsing());
2✔
529
         results.push_back(test_asn1_ucs2_parsing());
2✔
530
         results.push_back(test_asn1_ucs4_parsing());
2✔
531
         results.push_back(test_asn1_ucs_invalid_codepoint_rejection());
2✔
532
         results.push_back(test_asn1_ascii_encoding());
2✔
533
         results.push_back(test_asn1_utf8_encoding());
2✔
534
         results.push_back(test_asn1_tag_underlying_type());
2✔
535
         results.push_back(test_asn1_negative_int_encoding());
2✔
536
         results.push_back(test_der_constructed_tag_17_not_sorted());
2✔
537
         results.push_back(test_asn1_string_zero_length_roundtrip());
2✔
538
         results.push_back(test_pss_params_rejects_trailing_data_in_mgf1_params());
2✔
539

540
         return results;
1✔
541
      }
×
542
};
543

544
BOTAN_REGISTER_TEST("asn1", "asn1_encoding", ASN1_Tests);
545

546
class ASN1_Time_Parsing_Tests final : public Text_Based_Test {
×
547
   public:
548
      ASN1_Time_Parsing_Tests() : Text_Based_Test("asn1_time.vec", "Tspec") {}
2✔
549

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

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

555
         if(tag_str != "UTC" && tag_str != "UTC.invalid" && tag_str != "Generalized" &&
25✔
556
            tag_str != "Generalized.invalid") {
11✔
557
            throw Test_Error("Invalid tag value in ASN1 date parsing test");
×
558
         }
559

560
         const Botan::ASN1_Type tag = (tag_str == "UTC" || tag_str == "UTC.invalid")
24✔
561
                                         ? Botan::ASN1_Type::UtcTime
25✔
562
                                         : Botan::ASN1_Type::GeneralizedTime;
25✔
563

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

566
         if(valid) {
25✔
567
            const Botan::ASN1_Time time(tspec, tag);
13✔
568
            result.test_success("Accepted valid time");
13✔
569
         } else {
13✔
570
            result.test_throws("Invalid time rejected", [=]() { const Botan::ASN1_Time time(tspec, tag); });
60✔
571
         }
572

573
         return result;
25✔
574
      }
25✔
575
};
576

577
BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests);
578

579
class ASN1_String_Validation_Tests final : public Text_Based_Test {
×
580
   public:
581
      ASN1_String_Validation_Tests() :
1✔
582
            Text_Based_Test("asn1_string_validation.vec",
583
                            "Input,ValidNumeric,ValidPrintable,ValidIa5,ValidVisible,ValidUtf8") {}
2✔
584

585
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
7✔
586
         Test::Result result("ASN.1 string validation");
7✔
587

588
         const auto input = vars.get_req_str("Input");
7✔
589
         const bool valid_numeric = vars.get_req_bool("ValidNumeric");
7✔
590
         const bool valid_printable = vars.get_req_bool("ValidPrintable");
7✔
591
         const bool valid_ia5 = vars.get_req_bool("ValidIa5");
7✔
592
         const bool valid_visible = vars.get_req_bool("ValidVisible");
7✔
593
         const bool valid_utf8 = vars.get_req_bool("ValidUtf8");
7✔
594

595
         test_string_type(result, input, "NumericString", Botan::ASN1_Type::NumericString, valid_numeric);
7✔
596
         test_string_type(result, input, "PrintableString", Botan::ASN1_Type::PrintableString, valid_printable);
7✔
597
         test_string_type(result, input, "Ia5String", Botan::ASN1_Type::Ia5String, valid_ia5);
7✔
598
         test_string_type(result, input, "VisibleString", Botan::ASN1_Type::VisibleString, valid_visible);
7✔
599
         test_string_type(result, input, "Utf8String", Botan::ASN1_Type::Utf8String, valid_utf8);
7✔
600

601
         if(valid_utf8) {
7✔
602
            try {
7✔
603
               const Botan::ASN1_String str(input);
7✔
604
               const auto expected_tag =
14✔
605
                  valid_printable ? Botan::ASN1_Type::PrintableString : Botan::ASN1_Type::Utf8String;
7✔
606
               result.test_u32_eq("String tagging categorization",
7✔
607
                                  static_cast<uint32_t>(str.tagging()),
7✔
608
                                  static_cast<uint32_t>(expected_tag));
609
            } catch(const std::exception& ex) {
7✔
610
               result.test_failure(Botan::fmt("default constructor unexpectedly rejected '{}': {}", input, ex.what()));
×
611
            }
×
612
         }
613

614
         return result;
7✔
615
      }
7✔
616

617
   private:
618
      void test_string_type(Test::Result& result,
35✔
619
                            std::string_view input,
620
                            std::string_view type,
621
                            Botan::ASN1_Type tag,
622
                            bool expected_valid) {
623
         if(expected_valid) {
35✔
624
            try {
24✔
625
               const Botan::ASN1_String str(input, tag);
24✔
626
               result.test_str_eq(Botan::fmt("{} constructor value", type), str.value(), input);
24✔
627

628
               const auto enc = raw_encode_string(input, tag);
24✔
629
               Botan::BER_Decoder dec(enc);
24✔
630
               Botan::ASN1_String decoded;
24✔
631
               decoded.decode_from(dec);
24✔
632
               result.test_str_eq(Botan::fmt("{} decode value", type), decoded.value(), input);
48✔
633
            } catch(const std::exception& e) {
48✔
634
               result.test_failure(Botan::fmt("{} unexpectedly rejected '{}': {}", type, input, e.what()));
×
635
            }
×
636
         } else {
637
            result.test_throws(Botan::fmt("{} constructor rejects", type),
22✔
638
                               [&]() { const Botan::ASN1_String str(input, tag); });
22✔
639

640
            result.test_throws(Botan::fmt("{} decode rejects", type), [&]() {
22✔
641
               const auto enc = raw_encode_string(input, tag);
11✔
642
               Botan::BER_Decoder dec(enc);
11✔
643
               Botan::ASN1_String decoded;
11✔
644
               decoded.decode_from(dec);
11✔
645
            });
22✔
646
         }
647
      }
35✔
648

649
      static std::vector<uint8_t> raw_encode_string(std::string_view input, Botan::ASN1_Type tag) {
35✔
650
         std::vector<uint8_t> encoding;
35✔
651
         Botan::DER_Encoder der(encoding);
35✔
652
         der.add_object(tag, Botan::ASN1_Class::Universal, input);
35✔
653
         return encoding;
35✔
654
      }
35✔
655
};
656

657
BOTAN_REGISTER_TEST("asn1", "asn1_string_validation", ASN1_String_Validation_Tests);
658

659
class ASN1_Printer_Tests final : public Test {
1✔
660
   public:
661
      std::vector<Test::Result> run() override {
1✔
662
         Test::Result result("ASN1_Pretty_Printer");
1✔
663

664
         const Botan::ASN1_Pretty_Printer printer;
1✔
665

666
         const size_t num_tests = 7;
1✔
667

668
         for(size_t i = 1; i <= num_tests; ++i) {
8✔
669
            const std::string i_str = std::to_string(i);
7✔
670
            const std::vector<uint8_t> input_data = Test::read_binary_data_file("asn1_print/input" + i_str + ".der");
21✔
671
            const std::string expected_output = Test::read_data_file("asn1_print/output" + i_str + ".txt");
21✔
672

673
            try {
7✔
674
               const std::string output = printer.print(input_data);
7✔
675
               result.test_str_eq("Test " + i_str, output, expected_output);
14✔
676
            } catch(Botan::Exception& e) {
7✔
677
               result.test_failure(Botan::fmt("Printing test {} failed with an exception: '{}'", i, e.what()));
×
678
            }
×
679
         }
7✔
680

681
         return {result};
2✔
682
      }
9✔
683
};
684

685
BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests);
686

687
class ASN1_Decoding_Tests final : public Text_Based_Test {
×
688
   public:
689
      ASN1_Decoding_Tests() : Text_Based_Test("asn1_decoding.vec", "Input,ResultBER", "ResultDER") {}
2✔
690

691
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
55✔
692
         const auto input = vars.get_req_bin("Input");
55✔
693
         const std::string expected_ber = vars.get_req_str("ResultBER");
55✔
694
         const std::string expected_der = vars.get_opt_str("ResultDER", expected_ber);
55✔
695

696
         Test::Result result("ASN1 decoding");
55✔
697

698
         decoding_test(result, input, expected_ber, false);
55✔
699
         decoding_test(result, input, expected_der, true);
55✔
700

701
         return result;
110✔
702
      }
55✔
703

704
   private:
705
      static void decoding_test(Test::Result& result,
110✔
706
                                std::span<const uint8_t> input,
707
                                std::string_view expected,
708
                                bool require_der) {
709
         const Botan::ASN1_Pretty_Printer printer(4096, 2048, true, 0, 60, 64, require_der);
110✔
710
         const std::string mode = require_der ? "DER" : "BER";
165✔
711
         std::ostringstream sink;
110✔
712

713
         try {
110✔
714
            printer.print_to_stream(sink, input.data(), input.size());
110✔
715

716
            if(expected == "OK") {
52✔
717
               result.test_success();
52✔
718
            } else {
719
               result.test_failure(Botan::fmt("Accepted invalid {} input, expected error {}", mode, expected));
×
720
            }
721
         } catch(const std::exception& e) {
58✔
722
            if(expected == "OK") {
58✔
723
               result.test_failure(Botan::fmt("Rejected valid {} input with {}", mode, e.what()));
×
724
            } else {
725
               // BER_Decoding_Error prepends "BER: " to the message
726
               std::string msg = e.what();
58✔
727
               if(msg.starts_with("BER: ")) {
58✔
728
                  msg = msg.substr(5);
38✔
729
               }
730
               result.test_str_eq("error message", msg, expected);
58✔
731
            }
58✔
732
         }
58✔
733
      }
148✔
734
};
735

736
BOTAN_REGISTER_TEST("asn1", "asn1_decoding", ASN1_Decoding_Tests);
737

738
#endif
739

740
}  // namespace
741

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