Botan 3.0.0
Crypto and TLS for C&
asn1_print.cpp
Go to the documentation of this file.
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#include <botan/bigint.h>
9#include <botan/hex.h>
10#include <botan/der_enc.h>
11#include <botan/ber_dec.h>
12#include <botan/internal/fmt.h>
13#include <iomanip>
14#include <sstream>
15#include <cctype>
16
17namespace Botan {
18
19namespace {
20
21bool all_printable_chars(const uint8_t bits[], size_t bits_len)
22 {
23 for(size_t i = 0; i != bits_len; ++i)
24 {
25 int c = bits[i];
26 if(c > 127)
27 return false;
28
29 if((std::isalnum(c) || c == '.' || c == ':' || c == '/' || c == '-') == false)
30 return false;
31 }
32 return true;
33 }
34
35/*
36* Special hack to handle GeneralName [2] and [6] (DNS name and URI)
37*/
38bool possibly_a_general_name(const uint8_t bits[], size_t bits_len)
39 {
40 if(bits_len <= 2)
41 return false;
42
43 if(bits[0] != 0x82 && bits[0] != 0x86)
44 return false;
45
46 if(bits[1] != bits_len - 2)
47 return false;
48
49 if(all_printable_chars(bits + 2, bits_len - 2) == false)
50 return false;
51
52 return true;
53 }
54
55}
56
57std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const
58 {
59 std::ostringstream output;
60 print_to_stream(output, in, len);
61 return output.str();
62 }
63
64void ASN1_Formatter::print_to_stream(std::ostream& output,
65 const uint8_t in[],
66 size_t len) const
67 {
68 BER_Decoder dec(in, len);
69 decode(output, dec, 0);
70 }
71
72void ASN1_Formatter::decode(std::ostream& output,
73 BER_Decoder& decoder,
74 size_t level) const
75 {
76 BER_Object obj = decoder.get_next_object();
77
78 const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
79
80 while(obj.is_set())
81 {
82 const ASN1_Type type_tag = obj.type();
83 const ASN1_Class class_tag = obj.get_class();
84 const size_t length = obj.length();
85
86 /* hack to insert the tag+length back in front of the stuff now
87 that we've gotten the type info */
88 std::vector<uint8_t> bits;
89 DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
90
91 BER_Decoder data(bits);
92
94 {
95 BER_Decoder cons_info(obj.bits(), obj.length());
96
97 if(recurse_deeper)
98 {
99 output << format(type_tag, class_tag, level, length, "");
100 decode(output, cons_info, level + 1); // recurse
101 }
102 else
103 {
104 output << format(type_tag, class_tag, level, length,
105 format_bin(type_tag, class_tag, bits));
106 }
107 }
109 {
110 bool success_parsing_cs = false;
111
112 if(m_print_context_specific)
113 {
114 try
115 {
116 if(possibly_a_general_name(bits.data(), bits.size()))
117 {
118 output << format(type_tag, class_tag, level, level,
119 std::string(cast_uint8_ptr_to_char(&bits[2]), bits.size() - 2));
120 success_parsing_cs = true;
121 }
122 else if(recurse_deeper)
123 {
124 std::vector<uint8_t> inner_bits;
125 data.decode(inner_bits, type_tag);
126
127 BER_Decoder inner(inner_bits);
128 std::ostringstream inner_data;
129 decode(inner_data, inner, level + 1); // recurse
130 output << inner_data.str();
131 success_parsing_cs = true;
132 }
133 }
134 catch(...)
135 {
136 }
137 }
138
139 if(success_parsing_cs == false)
140 {
141 output << format(type_tag, class_tag, level, length,
142 format_bin(type_tag, class_tag, bits));
143 }
144 }
145 else if(type_tag == ASN1_Type::ObjectId)
146 {
147 OID oid;
148 data.decode(oid);
149
150 const std::string name = oid.human_name_or_empty();
151 const std::string oid_str = oid.to_string();
152
153 if(name.empty())
154 {
155 output << format(type_tag, class_tag, level, length, oid_str);
156 }
157 else
158 {
159 output << format(type_tag, class_tag, level, length,
160 fmt("{} [{}]", name, oid_str));
161 }
162 }
163 else if(type_tag == ASN1_Type::Integer || type_tag == ASN1_Type::Enumerated)
164 {
165 BigInt number;
166
167 if(type_tag == ASN1_Type::Integer)
168 {
169 data.decode(number);
170 }
171 else if(type_tag == ASN1_Type::Enumerated)
172 {
173 data.decode(number, ASN1_Type::Enumerated, class_tag);
174 }
175
176 output << format(type_tag, class_tag, level, length, format_bn(number));
177 }
178 else if(type_tag == ASN1_Type::Boolean)
179 {
180 bool boolean;
181 data.decode(boolean);
182 output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
183 }
184 else if(type_tag == ASN1_Type::Null)
185 {
186 output << format(type_tag, class_tag, level, length, "");
187 }
188 else if(type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString)
189 {
190 std::vector<uint8_t> decoded_bits;
191 data.decode(decoded_bits, type_tag);
192 bool printing_octet_string_worked = false;
193
194 if(recurse_deeper)
195 {
196 try
197 {
198 BER_Decoder inner(decoded_bits);
199
200 std::ostringstream inner_data;
201 decode(inner_data, inner, level + 1); // recurse
202
203 output << format(type_tag, class_tag, level, length, "");
204 output << inner_data.str();
205 printing_octet_string_worked = true;
206 }
207 catch(...)
208 {
209 }
210 }
211
212 if(!printing_octet_string_worked)
213 {
214 output << format(type_tag, class_tag, level, length,
215 format_bin(type_tag, class_tag, decoded_bits));
216 }
217 }
218 else if(ASN1_String::is_string_type(type_tag))
219 {
220 ASN1_String str;
221 data.decode(str);
222 output << format(type_tag, class_tag, level, length, str.value());
223 }
224 else if(type_tag == ASN1_Type::UtcTime || type_tag == ASN1_Type::GeneralizedTime)
225 {
226 ASN1_Time time;
227 data.decode(time);
228 output << format(type_tag, class_tag, level, length, time.readable_string());
229 }
230 else
231 {
232 output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag)
233 << " type=" << static_cast<int>(type_tag) << "\n";
234 }
235
236 obj = decoder.get_next_object();
237 }
238 }
239
240namespace {
241
242std::string format_type(ASN1_Type type_tag, ASN1_Class class_tag)
243 {
244 if(class_tag == ASN1_Class::Universal)
245 return asn1_tag_to_string(type_tag);
246
247 if(class_tag == ASN1_Class::Constructed && (type_tag == ASN1_Type::Sequence || type_tag == ASN1_Type::Set))
248 return asn1_tag_to_string(type_tag);
249
250 std::ostringstream oss;
251
252 if(intersects(class_tag, ASN1_Class::Constructed))
253 oss << "cons ";
254
255 oss << "[" << std::to_string(static_cast<uint32_t>(type_tag)) << "]";
256
257 if(intersects(class_tag, ASN1_Class::Application))
258 {
259 oss << " appl";
260 }
262 {
263 oss << " context";
264 }
265
266 return oss.str();
267 }
268
269}
270
271std::string ASN1_Pretty_Printer::format(ASN1_Type type_tag,
272 ASN1_Class class_tag,
273 size_t level,
274 size_t length,
275 std::string_view value) const
276 {
277 bool should_skip = false;
278
279 if(value.length() > m_print_limit)
280 {
281 should_skip = true;
282 }
283
284 if((type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) &&
285 value.length() > m_print_binary_limit)
286 {
287 should_skip = true;
288 }
289
290 level += m_initial_level;
291
292 std::ostringstream oss;
293
294 oss << " d=" << std::setw(2) << level
295 << ", l=" << std::setw(4) << length << ":"
296 << std::string(level + 1, ' ') << format_type(type_tag, class_tag);
297
298 if(!value.empty() && !should_skip)
299 {
300 const size_t current_pos = static_cast<size_t>(oss.tellp());
301 const size_t spaces_to_align =
302 (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
303
304 oss << std::string(spaces_to_align, ' ') << value;
305 }
306
307 oss << "\n";
308
309 return oss.str();
310 }
311
312std::string ASN1_Pretty_Printer::format_bin(ASN1_Type /*type_tag*/,
313 ASN1_Class /*class_tag*/,
314 const std::vector<uint8_t>& vec) const
315 {
316 if(all_printable_chars(vec.data(), vec.size()))
317 {
318 return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size());
319 }
320 else
321 return hex_encode(vec);
322 }
323
324std::string ASN1_Pretty_Printer::format_bn(const BigInt& bn) const
325 {
326 if(bn.bits() < 16)
327 return bn.to_dec_string();
328 else
329 return bn.to_hex_string();
330 }
331
332}
virtual std::string format_bin(ASN1_Type type_tag, ASN1_Class class_tag, const std::vector< uint8_t > &vec) const =0
virtual std::string format(ASN1_Type type_tag, ASN1_Class class_tag, size_t level, size_t length, std::string_view value) const =0
std::string print(const uint8_t in[], size_t len) const
Definition: asn1_print.cpp:57
void print_to_stream(std::ostream &out, const uint8_t in[], size_t len) const
Definition: asn1_print.cpp:64
virtual std::string format_bn(const BigInt &bn) const =0
static bool is_string_type(ASN1_Type tag)
Definition: asn1_str.cpp:69
BER_Object get_next_object()
Definition: ber_dec.cpp:240
size_t length() const
Definition: asn1_obj.h:157
const uint8_t * bits() const
Definition: asn1_obj.h:155
bool is_set() const
Definition: asn1_obj.h:145
ASN1_Type type() const
Definition: asn1_obj.h:152
ASN1_Class get_class() const
Definition: asn1_obj.h:153
DER_Encoder & add_object(ASN1_Type type_tag, ASN1_Class class_tag, const uint8_t rep[], size_t length)
Definition: der_enc.cpp:254
std::string name
Definition: alg_id.cpp:12
std::string asn1_tag_to_string(ASN1_Type type)
Definition: asn1_obj.cpp:109
ASN1_Class
Definition: asn1_obj.h:28
std::string fmt(std::string_view format, const T &... args)
Definition: fmt.h:60
ASN1_Type
Definition: asn1_obj.h:43
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition: hex.cpp:33
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition: mem_ops.h:188
bool intersects(ASN1_Class x, ASN1_Class y)
Definition: asn1_obj.h:70