aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli/asn1.cpp
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-11-15 11:29:06 -0500
committerJack Lloyd <[email protected]>2017-11-15 16:50:53 -0500
commite2217197ac766d11a28ee1389e414ca8bbb0b294 (patch)
treea60e1b3490c0325b3d6c9a8b7881e6d1c8f225cd /src/cli/asn1.cpp
parentbe4415ebb69a4232605d71f2a6b7df2d1cb114ee (diff)
Encapsulate ASN1 printer logic in a class
Diffstat (limited to 'src/cli/asn1.cpp')
-rw-r--r--src/cli/asn1.cpp296
1 files changed, 163 insertions, 133 deletions
diff --git a/src/cli/asn1.cpp b/src/cli/asn1.cpp
index 08c2562ba..13bcb94a9 100644
--- a/src/cli/asn1.cpp
+++ b/src/cli/asn1.cpp
@@ -16,90 +16,15 @@
#include <botan/asn1_str.h>
#include <botan/oids.h>
#include <botan/pem.h>
-#include <botan/charset.h>
#include <iomanip>
#include <sstream>
-#include <ctype.h>
+#include <cctype>
namespace Botan_CLI {
namespace {
-std::string url_encode(const std::vector<uint8_t>& in)
- {
- std::ostringstream out;
-
- size_t unprintable = 0;
-
- for(size_t i = 0; i != in.size(); ++i)
- {
- const int c = in[i];
- if(::isprint(c))
- {
- out << static_cast<char>(c);
- }
- else
- {
- out << "%" << std::hex << static_cast<int>(c) << std::dec;
- ++unprintable;
- }
- }
-
- if(unprintable >= in.size() / 4)
- {
- return Botan::hex_encode(in);
- }
-
- return out.str();
- }
-
-void emit(std::ostream& out,
- const std::string& type,
- size_t level, size_t length,
- const std::string& value = "")
- {
- // TODO make these configurable
- const size_t LIMIT = 4 * 1024;
- const size_t BIN_LIMIT = 1024;
-
- std::streampos starting_pos = out.tellp();
-
- out << " d=" << std::setw(2) << level
- << ", l=" << std::setw(4) << length << ": ";
-
- for(size_t i = 0; i != level; ++i)
- {
- out << ' ';
- }
-
- out << type;
-
- bool should_skip = false;
-
- if(value.length() > LIMIT)
- {
- should_skip = true;
- }
-
- if((type == "OCTET STRING" || type == "BIT STRING") && value.length() > BIN_LIMIT)
- {
- should_skip = true;
- }
-
- if(value != "" && !should_skip)
- {
- while(out.tellp() - starting_pos < 50)
- {
- out << ' ';
- }
-
- out << value;
- }
-
- out << "\n";
- }
-
std::string type_name(Botan::ASN1_Tag type)
{
switch(type)
@@ -157,9 +82,139 @@ std::string type_name(Botan::ASN1_Tag type)
}
}
-void decode(std::ostream& output,
- Botan::BER_Decoder& decoder,
- size_t level)
+class ASN1_Pretty_Printer
+ {
+ public:
+ ASN1_Pretty_Printer(size_t print_limit = 256,
+ size_t print_binary_limit = 256,
+ bool print_context_specific = true,
+ size_t initial_level = 0,
+ size_t value_column = 50) :
+ m_print_limit(print_limit),
+ m_print_binary_limit(print_binary_limit),
+ m_initial_level(initial_level),
+ m_value_column(value_column),
+ m_print_context_specific(print_context_specific)
+ {}
+
+ void print_to_stream(std::ostream& out,
+ const uint8_t in[],
+ size_t len) const;
+
+ std::string print(const uint8_t in[], size_t len) const;
+
+ template<typename Alloc>
+ std::string print(const std::vector<uint8_t, Alloc>& vec) const
+ {
+ return print(vec.data(), vec.size());
+ }
+
+ private:
+ void emit(std::ostream& out,
+ const std::string& type,
+ size_t level, size_t length,
+ const std::string& value = "") const;
+
+ void decode(std::ostream& output,
+ Botan::BER_Decoder& decoder,
+ size_t level) const;
+
+ std::string format_binary(const std::vector<uint8_t>& in) const;
+
+ const size_t m_print_limit;
+ const size_t m_print_binary_limit;
+ const size_t m_initial_level;
+ const size_t m_value_column;
+ const bool m_print_context_specific;
+ };
+
+std::string ASN1_Pretty_Printer::print(const uint8_t in[], size_t len) const
+ {
+ std::ostringstream out;
+ print_to_stream(out, in, len);
+ return out.str();
+ }
+
+void ASN1_Pretty_Printer::print_to_stream(std::ostream& out,
+ const uint8_t in[],
+ size_t len) const
+ {
+ Botan::BER_Decoder dec(in, len);
+ this->decode(out, dec, m_initial_level);
+ }
+
+std::string ASN1_Pretty_Printer::format_binary(const std::vector<uint8_t>& in) const
+ {
+ std::ostringstream out;
+
+ size_t unprintable = 0;
+
+ for(size_t i = 0; i != in.size(); ++i)
+ {
+ const int c = in[i];
+ if(std::isalnum(c))
+ {
+ out << static_cast<char>(c);
+ }
+ else
+ {
+ out << "%" << std::hex << static_cast<int>(c) << std::dec;
+ ++unprintable;
+ if(unprintable >= in.size() / 4)
+ {
+ return Botan::hex_encode(in);
+ }
+ }
+ }
+
+ return out.str();
+ }
+
+void ASN1_Pretty_Printer::emit(std::ostream& out,
+ const std::string& type,
+ size_t level, size_t length,
+ const std::string& value) const
+ {
+ std::streampos starting_pos = out.tellp();
+
+ out << " d=" << std::setw(2) << level
+ << ", l=" << std::setw(4) << length << ": ";
+
+ for(size_t i = 0; i != level; ++i)
+ {
+ out << ' ';
+ }
+
+ out << type;
+
+ bool should_skip = false;
+
+ if(value.length() > m_print_limit)
+ {
+ should_skip = true;
+ }
+
+ if((type == "OCTET STRING" || type == "BIT STRING") && value.length() > m_print_binary_limit)
+ {
+ should_skip = true;
+ }
+
+ if(value != "" && !should_skip)
+ {
+ while(static_cast<size_t>(out.tellp() - starting_pos) < m_value_column)
+ {
+ out << ' ';
+ }
+
+ out << value;
+ }
+
+ out << "\n";
+ }
+
+void ASN1_Pretty_Printer::decode(std::ostream& output,
+ Botan::BER_Decoder& decoder,
+ size_t level) const
{
Botan::BER_Object obj = decoder.get_next_object();
@@ -218,22 +273,25 @@ void decode(std::ostream& output,
}
else if((class_tag & Botan::APPLICATION) || (class_tag & Botan::CONTEXT_SPECIFIC))
{
-#if 0
- std::vector<uint8_t> bits;
- data.decode(out, bits, type_tag);
-
- try
+ if(m_print_context_specific)
{
- Botan::BER_Decoder inner(bits);
- decode(output, inner, level + 1); // recurse
+ std::vector<uint8_t> bits;
+ data.decode(bits, type_tag);
+
+ try
+ {
+ Botan::BER_Decoder inner(bits);
+ decode(output, inner, level + 1); // recurse
+ }
+ catch(...)
+ {
+ emit(output, "[" + std::to_string(type_tag) + "]", level, length, format_binary(bits));
+ }
}
- catch(...)
+ else
{
- emit(output, "[" + std::to_string(type_tag) + "]", level, length, url_encode(bits));
+ emit(output, "[" + std::to_string(type_tag) + "]", level, length, format_binary(bits));
}
-#else
- emit(output, "[" + std::to_string(type_tag) + "]", level, length, url_encode(bits));
-#endif
}
else if(type_tag == Botan::OBJECT_ID)
{
@@ -241,7 +299,11 @@ void decode(std::ostream& output,
data.decode(oid);
std::string out = Botan::OIDS::lookup(oid);
- if(out != oid.as_string())
+ if(out.empty())
+ {
+ out = oid.as_string();
+ }
+ else
{
out += " [" + oid.as_string() + "]";
}
@@ -291,7 +353,7 @@ void decode(std::ostream& output,
{
emit(output, type_name(type_tag), level, length);
}
- else if(type_tag == Botan::OCTET_STRING)
+ else if(type_tag == Botan::OCTET_STRING || type_tag == Botan::BIT_STRING)
{
std::vector<uint8_t> decoded_bits;
data.decode(decoded_bits, type_tag);
@@ -303,39 +365,9 @@ void decode(std::ostream& output,
}
catch(...)
{
- emit(output, type_name(type_tag), level, length, url_encode(decoded_bits));
+ emit(output, type_name(type_tag), level, length, format_binary(decoded_bits));
}
}
- else if(type_tag == Botan::BIT_STRING)
- {
- std::vector<uint8_t> decoded_bits;
- data.decode(decoded_bits, type_tag);
-
- std::vector<bool> bit_set;
-
- for(size_t i = 0; i != decoded_bits.size(); ++i)
- {
- for(size_t j = 0; j != 8; ++j)
- {
- const bool bit = static_cast<bool>((decoded_bits[decoded_bits.size() - i - 1] >> (7 - j)) & 1);
- bit_set.push_back(bit);
- }
- }
-
- std::string bit_str;
- for(size_t i = 0; i != bit_set.size(); ++i)
- {
- bool the_bit = bit_set[bit_set.size() - i - 1];
-
- if(!the_bit && bit_str.size() == 0)
- {
- continue;
- }
- bit_str += (the_bit ? "1" : "0");
- }
-
- emit(output, type_name(type_tag), level, length, bit_str);
- }
else if(Botan::ASN1_String::is_string_type(type_tag))
{
Botan::ASN1_String str;
@@ -358,14 +390,6 @@ void decode(std::ostream& output,
}
}
-std::string format_asn1(const uint8_t in[], size_t len)
- {
- std::ostringstream out;
- Botan::BER_Decoder dec(in, len);
- decode(out, dec, 0);
- return out.str();
- }
-
}
class ASN1_Printer final : public Command
@@ -389,7 +413,13 @@ class ASN1_Printer final : public Command
contents = slurp_file(input);
}
- output() << format_asn1(contents.data(), contents.size());
+ // TODO make these configurable
+ const size_t LIMIT = 4 * 1024;
+ const size_t BIN_LIMIT = 1024;
+ const bool PRINT_CONTEXT_SPECIFIC = true;
+
+ ASN1_Pretty_Printer printer(LIMIT, BIN_LIMIT, PRINT_CONTEXT_SPECIFIC);
+ output() << printer.print(contents);
}
};