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