Coverage Report

Created: 2025-11-24 06:12

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