1 #include <botan/botan.h>
2 #include <botan/bigint.h>
3 #include <botan/der_enc.h>
4 #include <botan/ber_dec.h>
5 #include <botan/asn1_obj.h>
6 #include <botan/oids.h>
7 #include <botan/pem.h>
8 #include <botan/charset.h>
9 using namespace Botan;
10
11 #include <stdio.h>
12 #include <ctype.h>
13
14 // Set this if your terminal understands UTF-8; otherwise output is in Latin-1
15 #define UTF8_TERMINAL 1
16
17 /*
18 What level the outermost layer of stuff is at. Probably 0 or 1; asn1parse
19 uses 0 as the outermost, while 1 makes more sense to me. 2+ doesn't make
20 much sense at all.
21 */
22 #define INITIAL_LEVEL 0
23
24 void decode(BER_Decoder&, size_t);
25 void emit(const std::string&, size_t, size_t, const std::string& = "");
26 std::string type_name(ASN1_Tag);
27
main(int argc,char * argv[])28 int main(int argc, char* argv[])
29 {
30 if(argc != 2)
31 {
32 printf("Usage: %s <file>\n", argv[0]);
33 return 1;
34 }
35
36 LibraryInitializer init;
37
38 try {
39 DataSource_Stream in(argv[1]);
40
41 if(!PEM_Code::matches(in))
42 {
43 BER_Decoder decoder(in);
44 decode(decoder, INITIAL_LEVEL);
45 }
46 else
47 {
48 std::string label; // ignored
49 BER_Decoder decoder(PEM_Code::decode(in, label));
50 decode(decoder, INITIAL_LEVEL);
51 }
52
53 }
54 catch(std::exception& e)
55 {
56 printf("%s\n", e.what());
57 return 1;
58 }
59 return 0;
60 }
61
decode(BER_Decoder & decoder,size_t level)62 void decode(BER_Decoder& decoder, size_t level)
63 {
64 BER_Object obj = decoder.get_next_object();
65
66 while(obj.type_tag != NO_OBJECT)
67 {
68 const ASN1_Tag type_tag = obj.type_tag;
69 const ASN1_Tag class_tag = obj.class_tag;
70 const size_t length = obj.value.size();
71
72 /* hack to insert the tag+length back in front of the stuff now
73 that we've gotten the type info */
74 DER_Encoder encoder;
75 encoder.add_object(type_tag, class_tag, obj.value, obj.value.size());
76 SecureVector<byte> bits = encoder.get_contents();
77
78 BER_Decoder data(bits);
79
80 if(class_tag & CONSTRUCTED)
81 {
82 BER_Decoder cons_info(obj.value);
83 if(type_tag == SEQUENCE)
84 {
85 emit("SEQUENCE", level, length);
86 decode(cons_info, level+1);
87 }
88 else if(type_tag == SET)
89 {
90 emit("SET", level, length);
91 decode(cons_info, level+1);
92 }
93 else
94 {
95 std::string name;
96
97 if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC) ||
98 (class_tag & PRIVATE))
99 {
100 name = "cons [" + to_string(type_tag) + "]";
101
102 if(class_tag & APPLICATION)
103 name += " appl";
104 if(class_tag & CONTEXT_SPECIFIC)
105 name += " context";
106 if(class_tag & PRIVATE)
107 name += " private";
108 }
109 else
110 name = type_name(type_tag) + " (cons)";
111
112 emit(name, level, length);
113 decode(cons_info, level+1);
114 }
115 }
116 else if(class_tag == APPLICATION || class_tag == CONTEXT_SPECIFIC ||
117 class_tag == PRIVATE)
118 {
119 bool not_text = false;
120
121 for(size_t i = 0; i != bits.size(); ++i)
122 if(!isgraph(bits[i]) && !isspace(bits[i]))
123 not_text = true;
124
125 Pipe pipe(((not_text) ? new Hex_Encoder : 0));
126 pipe.process_msg(bits);
127 emit("[" + to_string(type_tag) + "]", level, length,
128 pipe.read_all_as_string());
129 }
130 else if(type_tag == OBJECT_ID)
131 {
132 OID oid;
133 data.decode(oid);
134
135 std::string out = OIDS::lookup(oid);
136 if(out != oid.as_string())
137 out += " [" + oid.as_string() + "]";
138
139 emit(type_name(type_tag), level, length, out);
140 }
141 else if(type_tag == INTEGER)
142 {
143 BigInt number;
144 data.decode(number);
145
146 SecureVector<byte> rep;
147
148 /* If it's small, it's probably a number, not a hash */
149 if(number.bits() <= 16)
150 rep = BigInt::encode(number, BigInt::Decimal);
151 else
152 rep = BigInt::encode(number, BigInt::Hexadecimal);
153
154 std::string str;
155 for(size_t i = 0; i != rep.size(); ++i)
156 str += (char)rep[i];
157
158 emit(type_name(type_tag), level, length, str);
159 }
160 else if(type_tag == BOOLEAN)
161 {
162 bool boolean;
163 data.decode(boolean);
164 emit(type_name(type_tag),
165 level, length, (boolean ? "true" : "false"));
166 }
167 else if(type_tag == NULL_TAG)
168 {
169 emit(type_name(type_tag), level, length);
170 }
171 else if(type_tag == OCTET_STRING)
172 {
173 SecureVector<byte> bits;
174 data.decode(bits, type_tag);
175 bool not_text = false;
176
177 for(size_t i = 0; i != bits.size(); ++i)
178 if(!isgraph(bits[i]) && !isspace(bits[i]))
179 not_text = true;
180
181 Pipe pipe(((not_text) ? new Hex_Encoder : 0));
182 pipe.process_msg(bits);
183 emit(type_name(type_tag), level, length, pipe.read_all_as_string());
184 }
185 else if(type_tag == BIT_STRING)
186 {
187 SecureVector<byte> bits;
188 data.decode(bits, type_tag);
189
190 std::vector<bool> bit_set;
191
192 for(size_t i = 0; i != bits.size(); ++i)
193 for(size_t j = 0; j != 8; ++j)
194 bit_set.push_back((bool)((bits[bits.size()-i-1] >> (7-j)) & 1));
195
196 std::string bit_str;
197 for(size_t i = 0; i != bit_set.size(); ++i)
198 {
199 bool the_bit = bit_set[bit_set.size()-i-1];
200
201 if(!the_bit && bit_str.size() == 0)
202 continue;
203 bit_str += (the_bit ? "1" : "0");
204 }
205
206 emit(type_name(type_tag), level, length, bit_str);
207 }
208 else if(type_tag == PRINTABLE_STRING ||
209 type_tag == NUMERIC_STRING ||
210 type_tag == IA5_STRING ||
211 type_tag == T61_STRING ||
212 type_tag == VISIBLE_STRING ||
213 type_tag == UTF8_STRING ||
214 type_tag == BMP_STRING)
215 {
216 ASN1_String str;
217 data.decode(str);
218 if(UTF8_TERMINAL)
219 emit(type_name(type_tag), level, length,
220 Charset::transcode(str.iso_8859(),
221 LATIN1_CHARSET, UTF8_CHARSET));
222 else
223 emit(type_name(type_tag), level, length, str.iso_8859());
224 }
225 else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
226 {
227 X509_Time time;
228 data.decode(time);
229 emit(type_name(type_tag), level, length, time.readable_string());
230 }
231 else
232 fprintf(stderr, "Unknown tag: class=%02X, type=%02X\n",
233 class_tag, type_tag);
234
235 obj = decoder.get_next_object();
236 }
237 }
238
emit(const std::string & type,size_t level,size_t length,const std::string & value)239 void emit(const std::string& type, size_t level, size_t length,
240 const std::string& value)
241 {
242 const size_t LIMIT = 128;
243 const size_t BIN_LIMIT = 64;
244
245 int written = 0;
246 written += printf(" d=%2d, l=%4d: ", (int)level, (int)length);
247 for(size_t i = INITIAL_LEVEL; i != level; ++i)
248 written += printf(" ");
249 written += printf("%s ", type.c_str());
250
251 bool should_skip = false;
252 if(value.length() > LIMIT) should_skip = true;
253 if((type == "OCTET STRING" || type == "BIT STRING") &&
254 value.length() > BIN_LIMIT)
255 should_skip = true;
256
257 if(value != "" && !should_skip)
258 {
259 if(written % 2 == 0) printf(" ");
260 while(written < 50) written += printf(" ");
261 printf(":%s\n", value.c_str());
262 }
263 else
264 printf("\n");
265 }
266
type_name(ASN1_Tag type)267 std::string type_name(ASN1_Tag type)
268 {
269 if(type == PRINTABLE_STRING) return "PRINTABLE STRING";
270 if(type == NUMERIC_STRING) return "NUMERIC STRING";
271 if(type == IA5_STRING) return "IA5 STRING";
272 if(type == T61_STRING) return "T61 STRING";
273 if(type == UTF8_STRING) return "UTF8 STRING";
274 if(type == VISIBLE_STRING) return "VISIBLE STRING";
275 if(type == BMP_STRING) return "BMP STRING";
276
277 if(type == UTC_TIME) return "UTC TIME";
278 if(type == GENERALIZED_TIME) return "GENERALIZED TIME";
279
280 if(type == OCTET_STRING) return "OCTET STRING";
281 if(type == BIT_STRING) return "BIT STRING";
282
283 if(type == INTEGER) return "INTEGER";
284 if(type == NULL_TAG) return "NULL";
285 if(type == OBJECT_ID) return "OBJECT";
286 if(type == BOOLEAN) return "BOOLEAN";
287 return "(UNKNOWN)";
288 }
289