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

randombit / botan / 21653673286

04 Feb 2026 12:43AM UTC coverage: 90.073% (+0.002%) from 90.071%
21653673286

push

github

web-flow
Merge pull request #5279 from randombit/jack/asn1-time-h

Move ASN1_Time to its own header to avoid <chrono> inclusions

102237 of 113504 relevant lines covered (90.07%)

11507070.88 hits per line

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

87.24
/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/der_enc.h>
16
   #include <botan/internal/fmt.h>
17
#endif
18

19
namespace Botan_Tests {
20

21
#if defined(BOTAN_HAS_ASN1)
22

23
namespace {
24

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

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

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

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

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

43
   return result;
1✔
44
}
×
45

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

49
   // OSS-Fuzz #4353
50

51
   const Botan::ASN1_Pretty_Printer printer;
1✔
52

53
   size_t max_eoc_allowed = 0;
1✔
54

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

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

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

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

78
   return result;
1✔
79
}
1✔
80

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

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

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

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

101
   return result;
1✔
102
}
×
103

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

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

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

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

124
   return result;
1✔
125
}
×
126

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

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

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

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

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

148
   return result;
1✔
149
}
×
150

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

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

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

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

172
   return result;
1✔
173
}
×
174

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

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

183
      Botan::DER_Encoder enc;
1✔
184

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

188
      // \x13 - ASN1 tag for 'printable string'
189
      // \x06 - 6 characters of payload
190
      const auto moscowEncoded = Botan::hex_decode("13064D6F73636F77");
1✔
191
      result.test_eq("encoding result", encodingResult, moscowEncoded);
2✔
192

193
      result.test_success("No crash");
2✔
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
      const auto moscowEncoded = Botan::hex_decode("0C0CD09CD0BED181D0BAD0B2D0B0");
1✔
217
      result.test_eq("encoding result", encodingResult, moscowEncoded);
2✔
218

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

224
   return result;
1✔
225
}
×
226

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

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

242
   return result;
1✔
243
}
×
244

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

248
   BigInt n(32);
1✔
249

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

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

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

258
      result.test_eq("DER encoding round trips negative integers", n_dec, n);
2,048✔
259
   }
4,096✔
260

261
   return result;
1✔
262
}
1✔
263

264
}  // namespace
265

266
class ASN1_Tests final : public Test {
1✔
267
   public:
268
      std::vector<Test::Result> run() override {
1✔
269
         std::vector<Test::Result> results;
1✔
270

271
         results.push_back(test_ber_stack_recursion());
2✔
272
         results.push_back(test_ber_eoc_decoding_limits());
2✔
273
         results.push_back(test_asn1_utf8_ascii_parsing());
2✔
274
         results.push_back(test_asn1_utf8_parsing());
2✔
275
         results.push_back(test_asn1_ucs2_parsing());
2✔
276
         results.push_back(test_asn1_ucs4_parsing());
2✔
277
         results.push_back(test_asn1_ascii_encoding());
2✔
278
         results.push_back(test_asn1_utf8_encoding());
2✔
279
         results.push_back(test_asn1_tag_underlying_type());
2✔
280
         results.push_back(test_asn1_negative_int_encoding());
2✔
281

282
         return results;
1✔
283
      }
×
284
};
285

286
BOTAN_REGISTER_TEST("asn1", "asn1_encoding", ASN1_Tests);
287

288
class ASN1_Time_Parsing_Tests final : public Text_Based_Test {
×
289
   public:
290
      ASN1_Time_Parsing_Tests() : Text_Based_Test("asn1_time.vec", "Tspec") {}
2✔
291

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

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

297
         if(tag_str != "UTC" && tag_str != "UTC.invalid" && tag_str != "Generalized" &&
25✔
298
            tag_str != "Generalized.invalid") {
11✔
299
            throw Test_Error("Invalid tag value in ASN1 date parsing test");
×
300
         }
301

302
         const Botan::ASN1_Type tag = (tag_str == "UTC" || tag_str == "UTC.invalid")
24✔
303
                                         ? Botan::ASN1_Type::UtcTime
25✔
304
                                         : Botan::ASN1_Type::GeneralizedTime;
25✔
305

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

308
         if(valid) {
25✔
309
            const Botan::ASN1_Time time(tspec, tag);
13✔
310
            result.test_success("Accepted valid time");
26✔
311
         } else {
13✔
312
            result.test_throws("Invalid time rejected", [=]() { const Botan::ASN1_Time time(tspec, tag); });
108✔
313
         }
314

315
         return result;
25✔
316
      }
25✔
317
};
318

319
BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests);
320

321
class ASN1_Printer_Tests final : public Test {
1✔
322
   public:
323
      std::vector<Test::Result> run() override {
1✔
324
         Test::Result result("ASN1_Pretty_Printer");
1✔
325

326
         const Botan::ASN1_Pretty_Printer printer;
1✔
327

328
         const size_t num_tests = 7;
1✔
329

330
         for(size_t i = 1; i <= num_tests; ++i) {
8✔
331
            const std::string i_str = std::to_string(i);
7✔
332
            const std::vector<uint8_t> input_data = Test::read_binary_data_file("asn1_print/input" + i_str + ".der");
21✔
333
            const std::string expected_output = Test::read_data_file("asn1_print/output" + i_str + ".txt");
21✔
334

335
            try {
7✔
336
               const std::string output = printer.print(input_data);
7✔
337
               result.test_eq("Test " + i_str, output, expected_output);
14✔
338
            } catch(Botan::Exception& e) {
7✔
339
               result.test_failure(Botan::fmt("Printing test {} failed with an exception: '{}'", i, e.what()));
×
340
            }
×
341
         }
14✔
342

343
         return {result};
2✔
344
      }
2✔
345
};
346

347
BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests);
348

349
#endif
350

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