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