Botan  2.19.1
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/oids.h>
13 #include <iomanip>
14 #include <sstream>
15 #include <cctype>
16 
17 namespace Botan {
18 
19 namespace {
20 
21 bool 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 */
38 bool 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 
57 std::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 
64 void 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 
72 void 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 
243 namespace {
244 
245 std::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 
274 std::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 
315 std::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 }
std::string asn1_tag_to_string(ASN1_Tag type)
Definition: asn1_obj.cpp:108
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition: hex.cpp:31
DER_Encoder & add_object(ASN1_Tag type_tag, ASN1_Tag class_tag, const uint8_t rep[], size_t length)
Definition: der_enc.cpp:249
ASN1_Tag type() const
Definition: asn1_obj.h:116
static bool is_string_type(ASN1_Tag tag)
Definition: asn1_str.cpp:68
size_t length() const
Definition: asn1_obj.h:121
static std::vector< uint8_t > encode(const BigInt &n)
Definition: bigint.h:770
BOTAN_UNSTABLE_API std::string oid2str_or_empty(const OID &oid)
Definition: oids.cpp:111
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
ASN1_Tag
Definition: asn1_obj.h:25
const uint8_t * bits() const
Definition: asn1_obj.h:119
std::string name
ASN1_Tag get_class() const
Definition: asn1_obj.h:117
Definition: alg_id.cpp:13
BER_Object get_next_object()
Definition: ber_dec.cpp:237
void print_to_stream(std::ostream &out, const uint8_t in[], size_t len) const
Definition: asn1_print.cpp:64
bool is_set() const
Definition: asn1_obj.h:112
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:195
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
Definition: asn1_print.cpp:57