Coverage Report

Created: 2026-01-10 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/asn1/asn1_print.cpp
Line
Count
Source
1
/*
2
* (C) 2014,2015,2017 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6
7
#include <botan/asn1_print.h>
8
9
#include <botan/ber_dec.h>
10
#include <botan/bigint.h>
11
#include <botan/der_enc.h>
12
#include <botan/hex.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/mem_utils.h>
15
#include <iomanip>
16
#include <sstream>
17
18
namespace Botan {
19
20
namespace {
21
22
// Printable here means fits into an ASN.1 "PRINTABLE STRING" type
23
8.60k
bool is_printable_char(char c) {
24
8.60k
   if(c >= 'a' && c <= 'z') {
25
1.88k
      return true;
26
1.88k
   }
27
28
6.71k
   if(c >= 'A' && c <= 'Z') {
29
745
      return true;
30
745
   }
31
32
5.97k
   if(c >= '0' && c <= '9') {
33
802
      return true;
34
802
   }
35
36
5.17k
   if(c == '.' || c == ':' || c == '/' || c == '-') {
37
4.81k
      return true;
38
4.81k
   }
39
40
357
   return false;
41
5.17k
}
42
43
974
bool all_printable_chars(const uint8_t bits[], size_t bits_len) {
44
9.22k
   for(size_t i = 0; i != bits_len; ++i) {
45
8.60k
      if(!is_printable_char(bits[i])) {
46
357
         return false;
47
357
      }
48
8.60k
   }
49
617
   return true;
50
974
}
51
52
/*
53
* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
54
*/
55
5.14k
bool possibly_a_general_name(const uint8_t bits[], size_t bits_len) {
56
5.14k
   if(bits_len <= 2) {
57
2.93k
      return false;
58
2.93k
   }
59
60
2.20k
   if(bits[0] != 0x82 && bits[0] != 0x86) {
61
1.14k
      return false;
62
1.14k
   }
63
64
1.06k
   if(bits[1] != bits_len - 2) {
65
89
      return false;
66
89
   }
67
68
974
   if(!all_printable_chars(bits + 2, bits_len - 2)) {
69
357
      return false;
70
357
   }
71
72
617
   return true;
73
974
}
74
75
}  // namespace
76
77
0
std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const {
78
0
   std::ostringstream output;
79
0
   print_to_stream(output, in, len);
80
0
   return output.str();
81
0
}
82
83
8.52k
void ASN1_Formatter::print_to_stream(std::ostream& output, const uint8_t in[], size_t len) const {
84
8.52k
   BER_Decoder dec(in, len);
85
8.52k
   decode(output, dec, 0);
86
8.52k
}
87
88
30.5k
void ASN1_Formatter::decode(std::ostream& output, BER_Decoder& decoder, size_t level) const {
89
30.5k
   BER_Object obj = decoder.get_next_object();
90
91
30.5k
   const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
92
93
218k
   while(obj.is_set()) {
94
187k
      const ASN1_Type type_tag = obj.type();
95
187k
      const ASN1_Class class_tag = obj.get_class();
96
187k
      const size_t length = obj.length();
97
98
      /* hack to insert the tag+length back in front of the stuff now
99
         that we've gotten the type info */
100
187k
      std::vector<uint8_t> bits;
101
187k
      DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
102
103
187k
      BER_Decoder data(bits);
104
105
187k
      if(intersects(class_tag, ASN1_Class::Constructed)) {
106
7.39k
         BER_Decoder cons_info(obj.bits(), obj.length());
107
108
7.39k
         if(recurse_deeper) {
109
7.18k
            output << format(type_tag, class_tag, level, length, "");
110
7.18k
            decode(output, cons_info, level + 1);  // recurse
111
7.18k
         } else {
112
218
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
113
218
         }
114
180k
      } else if(intersects(class_tag, ASN1_Class::Application) || intersects(class_tag, ASN1_Class::ContextSpecific)) {
115
5.14k
         bool success_parsing_cs = false;
116
117
5.14k
         if(m_print_context_specific) {
118
5.14k
            try {
119
5.14k
               if(possibly_a_general_name(bits.data(), bits.size())) {
120
617
                  output << format(type_tag, class_tag, level, level, bytes_to_string(std::span{bits}.subspan(2)));
121
617
                  success_parsing_cs = true;
122
4.52k
               } else if(recurse_deeper) {
123
4.26k
                  std::vector<uint8_t> inner_bits;
124
4.26k
                  data.decode(inner_bits, type_tag);
125
126
4.26k
                  BER_Decoder inner(inner_bits);
127
4.26k
                  std::ostringstream inner_data;
128
4.26k
                  decode(inner_data, inner, level + 1);  // recurse
129
4.26k
                  output << inner_data.str();
130
4.26k
                  success_parsing_cs = true;
131
4.26k
               }
132
5.14k
            } catch(...) {}
133
5.14k
         }
134
135
5.14k
         if(!success_parsing_cs) {
136
4.52k
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
137
4.52k
         }
138
174k
      } else if(type_tag == ASN1_Type::ObjectId) {
139
114k
         OID oid;
140
114k
         data.decode(oid);
141
142
114k
         const std::string name = oid.human_name_or_empty();
143
114k
         const std::string oid_str = oid.to_string();
144
145
114k
         if(name.empty()) {
146
69.6k
            output << format(type_tag, class_tag, level, length, oid_str);
147
69.6k
         } else {
148
45.1k
            output << format(type_tag, class_tag, level, length, fmt("{} [{}]", name, oid_str));
149
45.1k
         }
150
114k
      } else if(type_tag == ASN1_Type::Integer || type_tag == ASN1_Type::Enumerated) {
151
2.88k
         BigInt number;
152
153
2.88k
         if(type_tag == ASN1_Type::Integer) {
154
2.44k
            data.decode(number);
155
2.44k
         } else if(type_tag == ASN1_Type::Enumerated) {
156
447
            data.decode(number, ASN1_Type::Enumerated, class_tag);
157
447
         }
158
159
2.88k
         output << format(type_tag, class_tag, level, length, format_bn(number));
160
57.3k
      } else if(type_tag == ASN1_Type::Boolean) {
161
1.04k
         bool boolean = false;
162
1.04k
         data.decode(boolean);
163
1.04k
         output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
164
56.2k
      } else if(type_tag == ASN1_Type::Null) {
165
528
         output << format(type_tag, class_tag, level, length, "");
166
55.7k
      } else if(type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) {
167
15.5k
         std::vector<uint8_t> decoded_bits;
168
15.5k
         data.decode(decoded_bits, type_tag);
169
15.5k
         bool printing_octet_string_worked = false;
170
171
15.5k
         if(recurse_deeper) {
172
14.8k
            try {
173
14.8k
               BER_Decoder inner(decoded_bits);
174
175
14.8k
               std::ostringstream inner_data;
176
14.8k
               decode(inner_data, inner, level + 1);  // recurse
177
178
14.8k
               output << format(type_tag, class_tag, level, length, "");
179
14.8k
               output << inner_data.str();
180
14.8k
               printing_octet_string_worked = true;
181
14.8k
            } catch(...) {}
182
14.8k
         }
183
184
15.5k
         if(!printing_octet_string_worked) {
185
10.3k
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, decoded_bits));
186
10.3k
         }
187
40.1k
      } else if(ASN1_String::is_string_type(type_tag)) {
188
4.97k
         ASN1_String str;
189
4.97k
         data.decode(str);
190
4.97k
         output << format(type_tag, class_tag, level, length, str.value());
191
35.1k
      } else if(type_tag == ASN1_Type::UtcTime || type_tag == ASN1_Type::GeneralizedTime) {
192
5.12k
         ASN1_Time time;
193
5.12k
         data.decode(time);
194
5.12k
         output << format(type_tag, class_tag, level, length, time.readable_string());
195
30.0k
      } else {
196
30.0k
         output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag) << " type=" << static_cast<int>(type_tag)
197
30.0k
                << "\n";
198
30.0k
      }
199
200
187k
      obj = decoder.get_next_object();
201
187k
   }
202
30.5k
}
203
204
namespace {
205
206
0
std::string format_type(ASN1_Type type_tag, ASN1_Class class_tag) {
207
0
   if(class_tag == ASN1_Class::Universal) {
208
0
      return asn1_tag_to_string(type_tag);
209
0
   }
210
211
0
   if(class_tag == ASN1_Class::Constructed && (type_tag == ASN1_Type::Sequence || type_tag == ASN1_Type::Set)) {
212
0
      return asn1_tag_to_string(type_tag);
213
0
   }
214
215
0
   std::ostringstream oss;
216
217
0
   if(intersects(class_tag, ASN1_Class::Constructed)) {
218
0
      oss << "cons ";
219
0
   }
220
221
0
   oss << "[" << std::to_string(static_cast<uint32_t>(type_tag)) << "]";
222
223
0
   if(intersects(class_tag, ASN1_Class::Application)) {
224
0
      oss << " appl";
225
0
   }
226
0
   if(intersects(class_tag, ASN1_Class::ContextSpecific)) {
227
0
      oss << " context";
228
0
   }
229
230
0
   return oss.str();
231
0
}
232
233
}  // namespace
234
235
std::string ASN1_Pretty_Printer::format(
236
0
   ASN1_Type type_tag, ASN1_Class class_tag, size_t level, size_t length, std::string_view value) const {
237
0
   bool should_skip = false;
238
239
0
   if(value.length() > m_print_limit) {
240
0
      should_skip = true;
241
0
   }
242
243
0
   if((type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) &&
244
0
      value.length() > m_print_binary_limit) {
245
0
      should_skip = true;
246
0
   }
247
248
0
   level += m_initial_level;
249
250
0
   std::ostringstream oss;
251
252
0
   oss << "  d=" << std::setw(2) << level << ", l=" << std::setw(4) << length << ":" << std::string(level + 1, ' ')
253
0
       << format_type(type_tag, class_tag);
254
255
0
   if(!value.empty() && !should_skip) {
256
0
      const size_t current_pos = static_cast<size_t>(oss.tellp());
257
0
      const size_t spaces_to_align = (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
258
259
0
      oss << std::string(spaces_to_align, ' ') << value;
260
0
   }
261
262
0
   oss << "\n";
263
264
0
   return oss.str();
265
0
}
266
267
std::string ASN1_Pretty_Printer::format_bin(ASN1_Type /*type_tag*/,
268
                                            ASN1_Class /*class_tag*/,
269
0
                                            const std::vector<uint8_t>& vec) const {
270
0
   if(all_printable_chars(vec.data(), vec.size())) {
271
0
      return bytes_to_string(vec);
272
0
   } else {
273
0
      return hex_encode(vec);
274
0
   }
275
0
}
276
277
0
std::string ASN1_Pretty_Printer::format_bn(const BigInt& bn) const {
278
0
   if(bn.bits() < 16) {
279
0
      return bn.to_dec_string();
280
0
   } else {
281
0
      return bn.to_hex_string();
282
0
   }
283
0
}
284
285
}  // namespace Botan