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