Botan 3.13.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/asn1_time.h>
10#include <botan/ber_dec.h>
11#include <botan/bigint.h>
12#include <botan/der_enc.h>
13#include <botan/hex.h>
14#include <botan/internal/charset.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/mem_utils.h>
17#include <iomanip>
18#include <sstream>
19
20namespace Botan {
21
22namespace {
23
24bool all_printable_chars(const uint8_t bits[], size_t bits_len) {
25 // Printable here means fits into an ASN.1 "PRINTABLE STRING" type
26 constexpr auto is_printable_char = CharacterValidityTable::alpha_numeric_plus(".:/-");
27
28 for(size_t i = 0; i != bits_len; ++i) {
29 if(!is_printable_char(bits[i])) {
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)) {
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 // The pretty printer is a best-effort diagnostic tool, so in BER mode it
69 // tolerates standalone EOC markers emitted by some BER producers.
70 const auto decoder_limits =
72 BER_Decoder dec(std::span<const uint8_t>{in, len}, decoder_limits);
73 decode(output, dec, 0);
74}
75
76void ASN1_Formatter::decode(std::ostream& output, BER_Decoder& decoder, size_t level) const {
77 BER_Object obj = decoder.get_next_object();
78
79 const bool recurse_deeper = (m_max_depth == 0 || level < m_max_depth);
80
81 while(obj.is_set()) {
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 if(intersects(class_tag, ASN1_Class::Constructed)) {
87 if(recurse_deeper) {
88 output << format(type_tag, class_tag, level, length, "");
89 // Move (not copy) the content into the sub-decoder; copying at every
90 // nesting level lets deeply nested input exhaust memory.
91 BER_Decoder cons_info(std::move(obj), decoder.limits());
92 decode(output, cons_info, level + 1); // recurse
93 } else {
94 std::vector<uint8_t> bits;
95 DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
96 output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
97 }
98
99 obj = decoder.get_next_object();
100 continue;
101 }
102
103 /* hack to insert the tag+length back in front of the stuff now
104 that we've gotten the type info */
105 std::vector<uint8_t> bits;
106 DER_Encoder(bits).add_object(type_tag, class_tag, obj.bits(), obj.length());
107
108 BER_Decoder data(bits, decoder.limits());
109
111 bool success_parsing_cs = false;
112
113 if(m_print_context_specific) {
114 try {
115 if(possibly_a_general_name(bits.data(), bits.size())) {
116 output << format(type_tag, class_tag, level, length, bytes_to_string(std::span{bits}.subspan(2)));
117 success_parsing_cs = true;
118 } else if(recurse_deeper) {
119 std::vector<uint8_t> inner_bits;
120 data.decode(inner_bits, type_tag);
121
122 BER_Decoder inner(inner_bits, decoder.limits());
123 std::ostringstream inner_data;
124 decode(inner_data, inner, level + 1); // recurse
125 output << inner_data.str();
126 success_parsing_cs = true;
127 }
128 } catch(...) {}
129 }
130
131 if(!success_parsing_cs) {
132 output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, bits));
133 }
134 } else if(type_tag == ASN1_Type::ObjectId) {
135 OID oid;
136 data.decode(oid);
137
138 const std::string oid_str = oid.to_string();
139
140 if(const auto name = oid.registered_name()) {
141 output << format(type_tag, class_tag, level, length, fmt("{} [{}]", *name, oid_str));
142 } else {
143 output << format(type_tag, class_tag, level, length, oid_str);
144 }
145 } else if(type_tag == ASN1_Type::Integer || type_tag == ASN1_Type::Enumerated) {
146 BigInt number;
147
148 if(type_tag == ASN1_Type::Integer) {
149 data.decode(number);
150 } else if(type_tag == ASN1_Type::Enumerated) {
151 data.decode(number, ASN1_Type::Enumerated, class_tag);
152 }
153
154 output << format(type_tag, class_tag, level, length, format_bn(number));
155 } else if(type_tag == ASN1_Type::Boolean) {
156 bool boolean = false;
157 data.decode(boolean);
158 output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
159 } else if(type_tag == ASN1_Type::Null) {
160 output << format(type_tag, class_tag, level, length, "");
161 } else if(type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) {
162 std::vector<uint8_t> decoded_bits;
163 data.decode(decoded_bits, type_tag);
164 bool printing_octet_string_worked = false;
165
166 if(recurse_deeper) {
167 try {
168 BER_Decoder inner(decoded_bits, decoder.limits());
169
170 std::ostringstream inner_data;
171 decode(inner_data, inner, level + 1); // recurse
172
173 output << format(type_tag, class_tag, level, length, "");
174 output << inner_data.str();
175 printing_octet_string_worked = true;
176 } catch(...) {}
177 }
178
179 if(!printing_octet_string_worked) {
180 output << format(type_tag, class_tag, level, length, format_bin(type_tag, class_tag, decoded_bits));
181 }
182 } else if(ASN1_String::is_string_type(type_tag)) {
183 ASN1_String str;
184 data.decode(str);
185 output << format(type_tag, class_tag, level, length, str.value());
186 } else if(type_tag == ASN1_Type::UtcTime || type_tag == ASN1_Type::GeneralizedTime) {
187 ASN1_Time time;
188 data.decode(time);
189 output << format(type_tag, class_tag, level, length, time.readable_string());
190 } else {
191 output << "Unknown ASN.1 tag class=" << static_cast<int>(class_tag) << " type=" << static_cast<int>(type_tag)
192 << "\n";
193 }
194
195 obj = decoder.get_next_object();
196 }
197}
198
199namespace {
200
201std::string format_type(ASN1_Type type_tag, ASN1_Class class_tag) {
202 if(class_tag == ASN1_Class::Universal) {
203 return asn1_tag_to_string(type_tag);
204 }
205
206 if(class_tag == ASN1_Class::Constructed && (type_tag == ASN1_Type::Sequence || type_tag == ASN1_Type::Set)) {
207 return asn1_tag_to_string(type_tag);
208 }
209
210 std::ostringstream oss;
211
212 if(intersects(class_tag, ASN1_Class::Constructed)) {
213 oss << "cons ";
214 }
215
216 oss << "[" << std::to_string(static_cast<uint32_t>(type_tag)) << "]";
217
218 if(intersects(class_tag, ASN1_Class::Application)) {
219 oss << " appl";
220 }
222 oss << " context";
223 }
224
225 return oss.str();
226}
227
228} // namespace
229
230std::string ASN1_Pretty_Printer::format(
231 ASN1_Type type_tag, ASN1_Class class_tag, size_t level, size_t length, std::string_view value) const {
232 bool should_skip = false;
233
234 if(value.length() > m_print_limit) {
235 should_skip = true;
236 }
237
238 if((type_tag == ASN1_Type::OctetString || type_tag == ASN1_Type::BitString) &&
239 value.length() > m_print_binary_limit) {
240 should_skip = true;
241 }
242
243 level += m_initial_level;
244
245 std::ostringstream oss;
246
247 oss << " d=" << std::setw(2) << level << ", l=" << std::setw(4) << length << ":" << std::string(level + 1, ' ')
248 << format_type(type_tag, class_tag);
249
250 if(!value.empty() && !should_skip) {
251 const size_t current_pos = static_cast<size_t>(oss.tellp());
252 const size_t spaces_to_align = (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
253
254 oss << std::string(spaces_to_align, ' ') << escape_control_chars(value);
255 }
256
257 oss << "\n";
258
259 return oss.str();
260}
261
262std::string ASN1_Pretty_Printer::format_bin(ASN1_Type /*type_tag*/,
263 ASN1_Class /*class_tag*/,
264 const std::vector<uint8_t>& vec) const {
265 // A value larger than the binary print limit is suppressed by format(), so
266 // skip the (potentially large) string/hex conversion entirely. vec.size() is
267 // a lower bound on the formatted length, so such a value is certainly dropped.
268 if(vec.size() > m_print_binary_limit) {
269 return "";
270 }
271
272 if(all_printable_chars(vec.data(), vec.size())) {
273 return bytes_to_string(vec);
274 } else {
275 return hex_encode(vec);
276 }
277}
278
279std::string ASN1_Pretty_Printer::format_bn(const BigInt& bn) const {
280 if(bn.bits() < 16) {
281 return bn.to_dec_string();
282 } else {
283 return bn.to_hex_string();
284 }
285}
286
287} // 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:131
static Limits BER(size_t max_nested_indef=16)
Definition ber_dec.h:49
Limits with_standalone_eoc_allowed() const
Definition ber_dec.h:95
static Limits DER()
Definition ber_dec.h:42
BER_Object get_next_object()
Definition ber_dec.cpp:516
Limits limits() const
Definition ber_dec.h:197
size_t length() const
Definition asn1_obj.h:303
const uint8_t * bits() const
Definition asn1_obj.h:298
bool is_set() const
Definition asn1_obj.h:267
ASN1_Type type() const
Definition asn1_obj.h:287
ASN1_Class get_class() const
Definition asn1_obj.h:292
static constexpr CharacterValidityTable alpha_numeric_plus(std::string_view extras)
Definition charset.h:114
std::string asn1_tag_to_string(ASN1_Type type)
Definition asn1_obj.cpp:129
ASN1_Class
Definition asn1_obj.h:32
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
ASN1_Type
Definition asn1_obj.h:47
std::string bytes_to_string(std::span< const uint8_t > bytes)
Definition mem_utils.h:76
void hex_encode(char output[], const uint8_t input[], size_t input_length, bool uppercase)
Definition hex.cpp:34
bool intersects(ASN1_Class x, ASN1_Class y)
Definition asn1_obj.h:77
std::string escape_control_chars(std::string_view utf8)
Definition charset.cpp:207