Botan 2.19.3
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/oids.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_Tag type_tag = obj.type();
83 const ASN1_Tag 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
93 if(class_tag & CONSTRUCTED)
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 }
108 else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
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 == OBJECT_ID)
146 {
147 OID oid;
148 data.decode(oid);
149
150 std::string out = OIDS::oid2str_or_empty(oid);
151 if(out.empty())
152 {
153 out = oid.to_string();
154 }
155 else
156 {
157 out += " [" + oid.to_string() + "]";
158 }
159
160 output << format(type_tag, class_tag, level, length, out);
161 }
162 else if(type_tag == INTEGER || type_tag == ENUMERATED)
163 {
164 BigInt number;
165
166 if(type_tag == INTEGER)
167 {
168 data.decode(number);
169 }
170 else if(type_tag == ENUMERATED)
171 {
172 data.decode(number, ENUMERATED, class_tag);
173 }
174
175 std::vector<uint8_t> rep = BigInt::encode(number);
176 if(rep.empty()) // if zero
177 rep.resize(1);
178
179 output << format(type_tag, class_tag, level, length, hex_encode(rep));
180 }
181 else if(type_tag == BOOLEAN)
182 {
183 bool boolean;
184 data.decode(boolean);
185 output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
186 }
187 else if(type_tag == NULL_TAG)
188 {
189 output << format(type_tag, class_tag, level, length, "");
190 }
191 else if(type_tag == OCTET_STRING || type_tag == BIT_STRING)
192 {
193 std::vector<uint8_t> decoded_bits;
194 data.decode(decoded_bits, type_tag);
195 bool printing_octet_string_worked = false;
196
197 if(recurse_deeper)
198 {
199 try
200 {
201 BER_Decoder inner(decoded_bits);
202
203 std::ostringstream inner_data;
204 decode(inner_data, inner, level + 1); // recurse
205
206 output << format(type_tag, class_tag, level, length, "");
207 output << inner_data.str();
208 printing_octet_string_worked = true;
209 }
210 catch(...)
211 {
212 }
213 }
214
215 if(!printing_octet_string_worked)
216 {
217 output << format(type_tag, class_tag, level, length,
218 format_bin(type_tag, class_tag, decoded_bits));
219 }
220 }
221 else if(ASN1_String::is_string_type(type_tag))
222 {
223 ASN1_String str;
224 data.decode(str);
225 output << format(type_tag, class_tag, level, length, str.value());
226 }
227 else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
228 {
229 ASN1_Time time;
230 data.decode(time);
231 output << format(type_tag, class_tag, level, length, time.readable_string());
232 }
233 else
234 {
235 output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag)
236 << " type=" << static_cast<int>(type_tag) << "\n";
237 }
238
239 obj = decoder.get_next_object();
240 }
241 }
242
243namespace {
244
245std::string format_type(ASN1_Tag type_tag, ASN1_Tag class_tag)
246 {
247 if(class_tag == UNIVERSAL)
248 return asn1_tag_to_string(type_tag);
249
250 if(class_tag == CONSTRUCTED && (type_tag == SEQUENCE || type_tag == SET))
251 return asn1_tag_to_string(type_tag);
252
253 std::string name;
254
255 if(class_tag & CONSTRUCTED)
256 name += "cons ";
257
258 name += "[" + std::to_string(type_tag) + "]";
259
260 if(class_tag & APPLICATION)
261 {
262 name += " appl";
263 }
264 if(class_tag & CONTEXT_SPECIFIC)
265 {
266 name += " context";
267 }
268
269 return name;
270 }
271
272}
273
274std::string ASN1_Pretty_Printer::format(ASN1_Tag type_tag,
275 ASN1_Tag class_tag,
276 size_t level,
277 size_t length,
278 const std::string& value) const
279 {
280 bool should_skip = false;
281
282 if(value.length() > m_print_limit)
283 {
284 should_skip = true;
285 }
286
287 if((type_tag == OCTET_STRING || type_tag == BIT_STRING) &&
288 value.length() > m_print_binary_limit)
289 {
290 should_skip = true;
291 }
292
293 level += m_initial_level;
294
295 std::ostringstream oss;
296
297 oss << " d=" << std::setw(2) << level
298 << ", l=" << std::setw(4) << length << ":"
299 << std::string(level + 1, ' ') << format_type(type_tag, class_tag);
300
301 if(value != "" && !should_skip)
302 {
303 const size_t current_pos = static_cast<size_t>(oss.tellp());
304 const size_t spaces_to_align =
305 (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
306
307 oss << std::string(spaces_to_align, ' ') << value;
308 }
309
310 oss << "\n";
311
312 return oss.str();
313 }
314
315std::string ASN1_Pretty_Printer::format_bin(ASN1_Tag /*type_tag*/,
316 ASN1_Tag /*class_tag*/,
317 const std::vector<uint8_t>& vec) const
318 {
319 if(all_printable_chars(vec.data(), vec.size()))
320 {
321 return std::string(cast_uint8_ptr_to_char(vec.data()), vec.size());
322 }
323 else
324 return hex_encode(vec);
325 }
326
327}
virtual std::string format(ASN1_Tag type_tag, ASN1_Tag class_tag, size_t level, size_t length, const std::string &value) const =0
virtual std::string format_bin(ASN1_Tag type_tag, ASN1_Tag class_tag, const std::vector< uint8_t > &vec) const =0
std::string print(const uint8_t in[], size_t len) const
void print_to_stream(std::ostream &out, const uint8_t in[], size_t len) const
static bool is_string_type(ASN1_Tag tag)
Definition asn1_str.cpp:68
BER_Object get_next_object()
Definition ber_dec.cpp:237
size_t length() const
Definition asn1_obj.h:121
ASN1_Tag type() const
Definition asn1_obj.h:116
const uint8_t * bits() const
Definition asn1_obj.h:119
bool is_set() const
Definition asn1_obj.h:112
ASN1_Tag get_class() const
Definition asn1_obj.h:117
static std::vector< uint8_t > encode(const BigInt &n)
Definition bigint.h:770
DER_Encoder & add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const uint8_t rep[], size_t length)
Definition der_enc.cpp:249
std::string name
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition oids.cpp:111
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:31
const char * cast_uint8_ptr_to_char(const uint8_t *b)
Definition mem_ops.h:195
ASN1_Tag
Definition asn1_obj.h:25
@ UTC_TIME
Definition asn1_obj.h:54
@ CONSTRUCTED
Definition asn1_obj.h:30
@ GENERALIZED_TIME
Definition asn1_obj.h:55
@ BIT_STRING
Definition asn1_obj.h:37
@ SEQUENCE
Definition asn1_obj.h:42
@ APPLICATION
Definition asn1_obj.h:27
@ BOOLEAN
Definition asn1_obj.h:35
@ CONTEXT_SPECIFIC
Definition asn1_obj.h:28
@ ENUMERATED
Definition asn1_obj.h:41
@ OCTET_STRING
Definition asn1_obj.h:38
@ NULL_TAG
Definition asn1_obj.h:39
@ UNIVERSAL
Definition asn1_obj.h:26
@ OBJECT_ID
Definition asn1_obj.h:40
@ SET
Definition asn1_obj.h:43
@ INTEGER
Definition asn1_obj.h:36
std::string asn1_tag_to_string(ASN1_Tag type)
Definition asn1_obj.cpp:108