Coverage Report

Created: 2025-04-11 06:34

/src/botan/src/lib/asn1/asn1_print.cpp
Line
Count
Source (jump to first uncovered line)
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/mem_ops.h>
14
#include <botan/internal/fmt.h>
15
#include <cctype>
16
#include <iomanip>
17
#include <sstream>
18
19
namespace Botan {
20
21
namespace {
22
23
1.11k
bool all_printable_chars(const uint8_t bits[], size_t bits_len) {
24
12.8k
   for(size_t i = 0; i != bits_len; ++i) {
25
12.1k
      int c = bits[i];
26
12.1k
      if(c > 127) {
27
212
         return false;
28
212
      }
29
30
11.9k
      if((std::isalnum(c) || c == '.' || c == ':' || c == '/' || c == '-') == false) {
31
244
         return false;
32
244
      }
33
11.9k
   }
34
660
   return true;
35
1.11k
}
36
37
/*
38
* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
39
*/
40
4.75k
bool possibly_a_general_name(const uint8_t bits[], size_t bits_len) {
41
4.75k
   if(bits_len <= 2) {
42
2.39k
      return false;
43
2.39k
   }
44
45
2.35k
   if(bits[0] != 0x82 && bits[0] != 0x86) {
46
1.14k
      return false;
47
1.14k
   }
48
49
1.21k
   if(bits[1] != bits_len - 2) {
50
97
      return false;
51
97
   }
52
53
1.11k
   if(all_printable_chars(bits + 2, bits_len - 2) == false) {
54
456
      return false;
55
456
   }
56
57
660
   return true;
58
1.11k
}
59
60
}  // namespace
61
62
0
std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const {
63
0
   std::ostringstream output;
64
0
   print_to_stream(output, in, len);
65
0
   return output.str();
66
0
}
67
68
2.97k
void ASN1_Formatter::print_to_stream(std::ostream& output, const uint8_t in[], size_t len) const {
69
2.97k
   BER_Decoder dec(in, len);
70
2.97k
   decode(output, dec, 0);
71
2.97k
}
72
73
23.5k
void ASN1_Formatter::decode(std::ostream& output, BER_Decoder& decoder, size_t level) const {
74
23.5k
   BER_Object obj = decoder.get_next_object();
75
76
23.5k
   const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
77
78
93.2k
   while(obj.is_set()) {
79
69.7k
      const ASN1_Type type_tag = obj.type();
80
69.7k
      const ASN1_Class class_tag = obj.get_class();
81
69.7k
      const size_t length = obj.length();
82
83
      /* hack to insert the tag+length back in front of the stuff now
84
         that we've gotten the type info */
85
69.7k
      std::vector<uint8_t> bits;
86
69.7k
      DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
87
88
69.7k
      BER_Decoder data(bits);
89
90
69.7k
      if(intersects(class_tag, ASN1_Class::Constructed)) {
91
6.05k
         BER_Decoder cons_info(obj.bits(), obj.length());
92
93
6.05k
         if(recurse_deeper) {
94
5.77k
            output << format(type_tag, class_tag, level, length, "");
95
5.77k
            decode(output, cons_info, level + 1);  // recurse
96
5.77k
         } else {
97
284
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
98
284
         }
99
63.6k
      } else if(intersects(class_tag, ASN1_Class::Application) || intersects(class_tag, ASN1_Class::ContextSpecific)) {
100
4.75k
         bool success_parsing_cs = false;
101
102
4.75k
         if(m_print_context_specific) {
103
4.75k
            try {
104
4.75k
               if(possibly_a_general_name(bits.data(), bits.size())) {
105
660
                  output << format(
106
660
                     type_tag, class_tag, level, level, std::string(cast_uint8_ptr_to_char(&bits[2]), bits.size() - 2));
107
660
                  success_parsing_cs = true;
108
4.09k
               } else if(recurse_deeper) {
109
3.83k
                  std::vector<uint8_t> inner_bits;
110
3.83k
                  data.decode(inner_bits, type_tag);
111
112
3.83k
                  BER_Decoder inner(inner_bits);
113
3.83k
                  std::ostringstream inner_data;
114
3.83k
                  decode(inner_data, inner, level + 1);  // recurse
115
3.83k
                  output << inner_data.str();
116
3.83k
                  success_parsing_cs = true;
117
3.83k
               }
118
4.75k
            } catch(...) {}
119
4.75k
         }
120
121
4.75k
         if(success_parsing_cs == false) {
122
4.09k
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
123
4.09k
         }
124
58.9k
      } else if(type_tag == ASN1_Type::ObjectId) {
125
5.00k
         OID oid;
126
5.00k
         data.decode(oid);
127
128
5.00k
         const std::string name = oid.human_name_or_empty();
129
5.00k
         const std::string oid_str = oid.to_string();
130
131
5.00k
         if(name.empty()) {
132
2.51k
            output << format(type_tag, class_tag, level, length, oid_str);
133
2.51k
         } else {
134
2.49k
            output << format(type_tag, class_tag, level, length, fmt("{} [{}]", name, oid_str));
135
2.49k
         }
136
53.9k
      } else if(type_tag == ASN1_Type::Integer || type_tag == ASN1_Type::Enumerated) {
137
1.75k
         BigInt number;
138
139
1.75k
         if(type_tag == ASN1_Type::Integer) {
140
1.47k
            data.decode(number);
141
1.47k
         } else if(type_tag == ASN1_Type::Enumerated) {
142
280
            data.decode(number, ASN1_Type::Enumerated, class_tag);
143
280
         }
144
145
1.75k
         output << format(type_tag, class_tag, level, length, format_bn(number));
146
52.1k
      } else if(type_tag == ASN1_Type::Boolean) {
147
1.01k
         bool boolean;
148
1.01k
         data.decode(boolean);
149
1.01k
         output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
150
51.1k
      } else if(type_tag == ASN1_Type::Null) {
151
348
         output << format(type_tag, class_tag, level, length, "");
152
50.7k
      } else if(type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) {
153
15.6k
         std::vector<uint8_t> decoded_bits;
154
15.6k
         data.decode(decoded_bits, type_tag);
155
15.6k
         bool printing_octet_string_worked = false;
156
157
15.6k
         if(recurse_deeper) {
158
14.7k
            try {
159
14.7k
               BER_Decoder inner(decoded_bits);
160
161
14.7k
               std::ostringstream inner_data;
162
14.7k
               decode(inner_data, inner, level + 1);  // recurse
163
164
14.7k
               output << format(type_tag, class_tag, level, length, "");
165
14.7k
               output << inner_data.str();
166
14.7k
               printing_octet_string_worked = true;
167
14.7k
            } catch(...) {}
168
14.7k
         }
169
170
15.6k
         if(!printing_octet_string_worked) {
171
9.64k
            output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, decoded_bits));
172
9.64k
         }
173
35.1k
      } else if(ASN1_String::is_string_type(type_tag)) {
174
4.43k
         ASN1_String str;
175
4.43k
         data.decode(str);
176
4.43k
         output << format(type_tag, class_tag, level, length, str.value());
177
30.7k
      } else if(type_tag == ASN1_Type::UtcTime || type_tag == ASN1_Type::GeneralizedTime) {
178
4.89k
         ASN1_Time time;
179
4.89k
         data.decode(time);
180
4.89k
         output << format(type_tag, class_tag, level, length, time.readable_string());
181
25.8k
      } else {
182
25.8k
         output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag) << " type=" << static_cast<int>(type_tag)
183
25.8k
                << "\n";
184
25.8k
      }
185
186
69.7k
      obj = decoder.get_next_object();
187
69.7k
   }
188
23.5k
}
189
190
namespace {
191
192
0
std::string format_type(ASN1_Type type_tag, ASN1_Class class_tag) {
193
0
   if(class_tag == ASN1_Class::Universal) {
194
0
      return asn1_tag_to_string(type_tag);
195
0
   }
196
197
0
   if(class_tag == ASN1_Class::Constructed && (type_tag == ASN1_Type::Sequence || type_tag == ASN1_Type::Set)) {
198
0
      return asn1_tag_to_string(type_tag);
199
0
   }
200
201
0
   std::ostringstream oss;
202
203
0
   if(intersects(class_tag, ASN1_Class::Constructed)) {
204
0
      oss << "cons ";
205
0
   }
206
207
0
   oss << "[" << std::to_string(static_cast<uint32_t>(type_tag)) << "]";
208
209
0
   if(intersects(class_tag, ASN1_Class::Application)) {
210
0
      oss << " appl";
211
0
   }
212
0
   if(intersects(class_tag, ASN1_Class::ContextSpecific)) {
213
0
      oss << " context";
214
0
   }
215
216
0
   return oss.str();
217
0
}
218
219
}  // namespace
220
221
std::string ASN1_Pretty_Printer::format(
222
0
   ASN1_Type type_tag, ASN1_Class class_tag, size_t level, size_t length, std::string_view value) const {
223
0
   bool should_skip = false;
224
225
0
   if(value.length() > m_print_limit) {
226
0
      should_skip = true;
227
0
   }
228
229
0
   if((type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) &&
230
0
      value.length() > m_print_binary_limit) {
231
0
      should_skip = true;
232
0
   }
233
234
0
   level += m_initial_level;
235
236
0
   std::ostringstream oss;
237
238
0
   oss << "  d=" << std::setw(2) << level << ", l=" << std::setw(4) << length << ":" << std::string(level + 1, ' ')
239
0
       << format_type(type_tag, class_tag);
240
241
0
   if(!value.empty() && !should_skip) {
242
0
      const size_t current_pos = static_cast<size_t>(oss.tellp());
243
0
      const size_t spaces_to_align = (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
244
245
0
      oss << std::string(spaces_to_align, ' ') << value;
246
0
   }
247
248
0
   oss << "\n";
249
250
0
   return oss.str();
251
0
}
252
253
std::string ASN1_Pretty_Printer::format_bin(ASN1_Type /*type_tag*/,
254
                                            ASN1_Class /*class_tag*/,
255
0
                                            const std::vector<uint8_t>& vec) const {
256
0
   if(all_printable_chars(vec.data(), vec.size())) {
257
0
      return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size());
258
0
   } else {
259
0
      return hex_encode(vec);
260
0
   }
261
0
}
262
263
0
std::string ASN1_Pretty_Printer::format_bn(const BigInt& bn) const {
264
0
   if(bn.bits() < 16) {
265
0
      return bn.to_dec_string();
266
0
   } else {
267
0
      return bn.to_hex_string();
268
0
   }
269
0
}
270
271
}  // namespace Botan