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