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

randombit / botan / 25457312714

06 May 2026 07:43PM UTC coverage: 89.331% (-2.3%) from 91.667%
25457312714

push

github

randombit
In TLS 1.3 verification of client certs, check the correct extension for OCSP

This was checking if the client asked us (the server) for OCSP, instead of
checking if we asked the client for OCSP when we sent the CertificateRequest.

107574 of 120422 relevant lines covered (89.33%)

11482758.98 hits per line

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

88.78
/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_ucs_invalid_codepoint_rejection() {
1✔
177
   Test::Result result("ASN.1 UCS-2/UCS-4 invalid codepoint rejection");
1✔
178

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

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

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

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

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

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

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

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

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

222
   return result;
1✔
223
}
×
224

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

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

233
      Botan::DER_Encoder enc;
1✔
234

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

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

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

247
   return result;
1✔
248
}
×
249

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

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

258
      Botan::DER_Encoder enc;
1✔
259

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

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

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

272
   return result;
1✔
273
}
×
274

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

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

290
   return result;
1✔
291
}
×
292

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

296
   BigInt n(32);
1✔
297

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

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

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

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

309
   return result;
1✔
310
}
1✔
311

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

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

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

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

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

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

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

347
   return result;
1✔
348
}
1✔
349

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

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

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

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

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

417
   return result;
1✔
418
}
×
419

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

423
   const size_t num_siblings = 4096;
1✔
424

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

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

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

446
   return result;
1✔
447
}
1✔
448

449
class ASN1_Tests final : public Test {
1✔
450
   public:
451
      std::vector<Test::Result> run() override {
1✔
452
         std::vector<Test::Result> results;
1✔
453

454
         results.push_back(test_ber_stack_recursion());
2✔
455
         results.push_back(test_ber_eoc_decoding_limits());
2✔
456
         results.push_back(test_ber_indefinite_length_trailing_data());
2✔
457
         results.push_back(test_ber_find_eoc());
2✔
458
         results.push_back(test_asn1_utf8_ascii_parsing());
2✔
459
         results.push_back(test_asn1_utf8_parsing());
2✔
460
         results.push_back(test_asn1_ucs2_parsing());
2✔
461
         results.push_back(test_asn1_ucs4_parsing());
2✔
462
         results.push_back(test_asn1_ucs_invalid_codepoint_rejection());
2✔
463
         results.push_back(test_asn1_ascii_encoding());
2✔
464
         results.push_back(test_asn1_utf8_encoding());
2✔
465
         results.push_back(test_asn1_tag_underlying_type());
2✔
466
         results.push_back(test_asn1_negative_int_encoding());
2✔
467
         results.push_back(test_der_constructed_tag_17_not_sorted());
2✔
468

469
         return results;
1✔
470
      }
×
471
};
472

473
BOTAN_REGISTER_TEST("asn1", "asn1_encoding", ASN1_Tests);
474

475
class ASN1_Time_Parsing_Tests final : public Text_Based_Test {
×
476
   public:
477
      ASN1_Time_Parsing_Tests() : Text_Based_Test("asn1_time.vec", "Tspec") {}
2✔
478

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

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

484
         if(tag_str != "UTC" && tag_str != "UTC.invalid" && tag_str != "Generalized" &&
25✔
485
            tag_str != "Generalized.invalid") {
11✔
486
            throw Test_Error("Invalid tag value in ASN1 date parsing test");
×
487
         }
488

489
         const Botan::ASN1_Type tag = (tag_str == "UTC" || tag_str == "UTC.invalid")
24✔
490
                                         ? Botan::ASN1_Type::UtcTime
25✔
491
                                         : Botan::ASN1_Type::GeneralizedTime;
25✔
492

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

495
         if(valid) {
25✔
496
            const Botan::ASN1_Time time(tspec, tag);
13✔
497
            result.test_success("Accepted valid time");
13✔
498
         } else {
13✔
499
            result.test_throws("Invalid time rejected", [=]() { const Botan::ASN1_Time time(tspec, tag); });
60✔
500
         }
501

502
         return result;
25✔
503
      }
25✔
504
};
505

506
BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests);
507

508
class ASN1_String_Validation_Tests final : public Text_Based_Test {
×
509
   public:
510
      ASN1_String_Validation_Tests() :
1✔
511
            Text_Based_Test("asn1_string_validation.vec",
512
                            "Input,ValidNumeric,ValidPrintable,ValidIa5,ValidVisible,ValidUtf8") {}
2✔
513

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

517
         const auto input = vars.get_req_str("Input");
7✔
518
         const bool valid_numeric = vars.get_req_bool("ValidNumeric");
7✔
519
         const bool valid_printable = vars.get_req_bool("ValidPrintable");
7✔
520
         const bool valid_ia5 = vars.get_req_bool("ValidIa5");
7✔
521
         const bool valid_visible = vars.get_req_bool("ValidVisible");
7✔
522
         const bool valid_utf8 = vars.get_req_bool("ValidUtf8");
7✔
523

524
         test_string_type(result, input, "NumericString", Botan::ASN1_Type::NumericString, valid_numeric);
7✔
525
         test_string_type(result, input, "PrintableString", Botan::ASN1_Type::PrintableString, valid_printable);
7✔
526
         test_string_type(result, input, "Ia5String", Botan::ASN1_Type::Ia5String, valid_ia5);
7✔
527
         test_string_type(result, input, "VisibleString", Botan::ASN1_Type::VisibleString, valid_visible);
7✔
528
         test_string_type(result, input, "Utf8String", Botan::ASN1_Type::Utf8String, valid_utf8);
7✔
529

530
         if(valid_utf8) {
7✔
531
            try {
7✔
532
               const Botan::ASN1_String str(input);
7✔
533
               const auto expected_tag =
14✔
534
                  valid_printable ? Botan::ASN1_Type::PrintableString : Botan::ASN1_Type::Utf8String;
7✔
535
               result.test_u32_eq("String tagging categorization",
7✔
536
                                  static_cast<uint32_t>(str.tagging()),
7✔
537
                                  static_cast<uint32_t>(expected_tag));
538
            } catch(const std::exception& ex) {
7✔
539
               result.test_failure(Botan::fmt("default constructor unexpectedly rejected '{}': {}", input, ex.what()));
×
540
            }
×
541
         }
542

543
         return result;
7✔
544
      }
7✔
545

546
   private:
547
      void test_string_type(Test::Result& result,
35✔
548
                            std::string_view input,
549
                            std::string_view type,
550
                            Botan::ASN1_Type tag,
551
                            bool expected_valid) {
552
         if(expected_valid) {
35✔
553
            try {
24✔
554
               const Botan::ASN1_String str(input, tag);
24✔
555
               result.test_str_eq(Botan::fmt("{} constructor value", type), str.value(), input);
24✔
556

557
               const auto enc = raw_encode_string(input, tag);
24✔
558
               Botan::BER_Decoder dec(enc);
24✔
559
               Botan::ASN1_String decoded;
24✔
560
               decoded.decode_from(dec);
24✔
561
               result.test_str_eq(Botan::fmt("{} decode value", type), decoded.value(), input);
48✔
562
            } catch(const std::exception& e) {
48✔
563
               result.test_failure(Botan::fmt("{} unexpectedly rejected '{}': {}", type, input, e.what()));
×
564
            }
×
565
         } else {
566
            result.test_throws(Botan::fmt("{} constructor rejects", type),
22✔
567
                               [&]() { const Botan::ASN1_String str(input, tag); });
22✔
568

569
            result.test_throws(Botan::fmt("{} decode rejects", type), [&]() {
22✔
570
               const auto enc = raw_encode_string(input, tag);
11✔
571
               Botan::BER_Decoder dec(enc);
11✔
572
               Botan::ASN1_String decoded;
11✔
573
               decoded.decode_from(dec);
11✔
574
            });
22✔
575
         }
576
      }
35✔
577

578
      static std::vector<uint8_t> raw_encode_string(std::string_view input, Botan::ASN1_Type tag) {
35✔
579
         std::vector<uint8_t> encoding;
35✔
580
         Botan::DER_Encoder der(encoding);
35✔
581
         der.add_object(tag, Botan::ASN1_Class::Universal, input);
35✔
582
         return encoding;
35✔
583
      }
35✔
584
};
585

586
BOTAN_REGISTER_TEST("asn1", "asn1_string_validation", ASN1_String_Validation_Tests);
587

588
class ASN1_Printer_Tests final : public Test {
1✔
589
   public:
590
      std::vector<Test::Result> run() override {
1✔
591
         Test::Result result("ASN1_Pretty_Printer");
1✔
592

593
         const Botan::ASN1_Pretty_Printer printer;
1✔
594

595
         const size_t num_tests = 7;
1✔
596

597
         for(size_t i = 1; i <= num_tests; ++i) {
8✔
598
            const std::string i_str = std::to_string(i);
7✔
599
            const std::vector<uint8_t> input_data = Test::read_binary_data_file("asn1_print/input" + i_str + ".der");
21✔
600
            const std::string expected_output = Test::read_data_file("asn1_print/output" + i_str + ".txt");
21✔
601

602
            try {
7✔
603
               const std::string output = printer.print(input_data);
7✔
604
               result.test_str_eq("Test " + i_str, output, expected_output);
14✔
605
            } catch(Botan::Exception& e) {
7✔
606
               result.test_failure(Botan::fmt("Printing test {} failed with an exception: '{}'", i, e.what()));
×
607
            }
×
608
         }
7✔
609

610
         return {result};
2✔
611
      }
9✔
612
};
613

614
BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests);
615

616
class ASN1_Decoding_Tests final : public Text_Based_Test {
×
617
   public:
618
      ASN1_Decoding_Tests() : Text_Based_Test("asn1_decoding.vec", "Input,ResultBER", "ResultDER") {}
2✔
619

620
      Test::Result run_one_test(const std::string& /*header*/, const VarMap& vars) override {
55✔
621
         const auto input = vars.get_req_bin("Input");
55✔
622
         const std::string expected_ber = vars.get_req_str("ResultBER");
55✔
623
         const std::string expected_der = vars.get_opt_str("ResultDER", expected_ber);
55✔
624

625
         Test::Result result("ASN1 decoding");
55✔
626

627
         decoding_test(result, input, expected_ber, false);
55✔
628
         decoding_test(result, input, expected_der, true);
55✔
629

630
         return result;
110✔
631
      }
55✔
632

633
   private:
634
      static void decoding_test(Test::Result& result,
110✔
635
                                std::span<const uint8_t> input,
636
                                std::string_view expected,
637
                                bool require_der) {
638
         const Botan::ASN1_Pretty_Printer printer(4096, 2048, true, 0, 60, 64, require_der);
110✔
639
         const std::string mode = require_der ? "DER" : "BER";
165✔
640
         std::ostringstream sink;
110✔
641

642
         try {
110✔
643
            printer.print_to_stream(sink, input.data(), input.size());
110✔
644

645
            if(expected == "OK") {
52✔
646
               result.test_success();
52✔
647
            } else {
648
               result.test_failure(Botan::fmt("Accepted invalid {} input, expected error {}", mode, expected));
×
649
            }
650
         } catch(const std::exception& e) {
58✔
651
            if(expected == "OK") {
58✔
652
               result.test_failure(Botan::fmt("Rejected valid {} input with {}", mode, e.what()));
×
653
            } else {
654
               // BER_Decoding_Error prepends "BER: " to the message
655
               std::string msg = e.what();
58✔
656
               if(msg.starts_with("BER: ")) {
58✔
657
                  msg = msg.substr(5);
38✔
658
               }
659
               result.test_str_eq("error message", msg, expected);
58✔
660
            }
58✔
661
         }
58✔
662
      }
148✔
663
};
664

665
BOTAN_REGISTER_TEST("asn1", "asn1_decoding", ASN1_Decoding_Tests);
666

667
#endif
668

669
}  // namespace
670

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