aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/fuzzer/asn1.cpp21
-rw-r--r--src/lib/asn1/asn1_obj.cpp6
-rw-r--r--src/lib/asn1/asn1_print.cpp248
-rw-r--r--src/lib/asn1/asn1_print.h86
4 files changed, 216 insertions, 145 deletions
diff --git a/src/fuzzer/asn1.cpp b/src/fuzzer/asn1.cpp
index 8fabad5ed..da3e1607f 100644
--- a/src/fuzzer/asn1.cpp
+++ b/src/fuzzer/asn1.cpp
@@ -8,6 +8,25 @@
#include <botan/asn1_print.h>
#include <fstream>
+class ASN1_Parser final : public Botan::ASN1_Formatter
+ {
+ public:
+ ASN1_Parser() : Botan::ASN1_Formatter(true) {}
+
+ protected:
+ std::string format(Botan::ASN1_Tag, Botan::ASN1_Tag, size_t, size_t,
+ const std::string&) const override
+ {
+ return "";
+ }
+
+ std::string format_bin(Botan::ASN1_Tag, Botan::ASN1_Tag,
+ const std::vector<uint8_t>&) const override
+ {
+ return "";
+ }
+ };
+
void fuzz(const uint8_t in[], size_t len)
{
try
@@ -17,7 +36,7 @@ void fuzz(const uint8_t in[], size_t len)
* on actual output formatting, no memory is allocated, etc.
*/
std::ofstream out;
- Botan::ASN1_Pretty_Printer printer;
+ ASN1_Parser printer;
printer.print_to_stream(out, in, len);
}
catch(Botan::Exception& e) { }
diff --git a/src/lib/asn1/asn1_obj.cpp b/src/lib/asn1/asn1_obj.cpp
index 1f93a4b8b..c83875ae5 100644
--- a/src/lib/asn1/asn1_obj.cpp
+++ b/src/lib/asn1/asn1_obj.cpp
@@ -16,6 +16,12 @@ std::string asn1_tag_to_string(ASN1_Tag type)
{
switch(type)
{
+ case Botan::SEQUENCE:
+ return "SEQUENCE";
+
+ case Botan::SET:
+ return "SET";
+
case Botan::PRINTABLE_STRING:
return "PRINTABLE STRING";
diff --git a/src/lib/asn1/asn1_print.cpp b/src/lib/asn1/asn1_print.cpp
index 979d041d8..ffb2eda48 100644
--- a/src/lib/asn1/asn1_print.cpp
+++ b/src/lib/asn1/asn1_print.cpp
@@ -18,89 +18,24 @@
namespace Botan {
-std::string ASN1_Pretty_Printer::print(const uint8_t in[], size_t len) const
+std::string ASN1_Formatter::print(const uint8_t in[], size_t len) const
{
std::ostringstream output;
print_to_stream(output, in, len);
return output.str();
}
-void ASN1_Pretty_Printer::print_to_stream(std::ostream& output,
- const uint8_t in[],
- size_t len) const
+void ASN1_Formatter::print_to_stream(std::ostream& output,
+ const uint8_t in[],
+ size_t len) const
{
BER_Decoder dec(in, len);
- decode(output, dec, m_initial_level);
+ decode(output, dec, 0);
}
-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 << "x" << std::hex << static_cast<int>(c) << std::dec;
- ++unprintable;
- if(unprintable >= in.size() / 4)
- {
- return hex_encode(in);
- }
- }
- }
-
- return out.str();
- }
-
-void ASN1_Pretty_Printer::emit(std::ostream& output,
- const std::string& type,
- size_t level, size_t length,
- const std::string& value) const
- {
- std::ostringstream oss;
-
- oss << " d=" << std::setw(2) << level
- << ", l=" << std::setw(4) << length << ":"
- << std::string(level + 1, ' ') << 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;
- }
-
- const std::string s = oss.str();
-
- output << s;
-
- if(value != "" && !should_skip)
- {
- const size_t spaces_to_align =
- (s.size() >= m_value_column) ? 1 : (m_value_column - s.size());
-
- output << std::string(spaces_to_align, ' ') << value;
- }
-
- output << "\n";
- }
-
-void ASN1_Pretty_Printer::decode(std::ostream& output,
- BER_Decoder& decoder,
- size_t level) const
+void ASN1_Formatter::decode(std::ostream& output,
+ BER_Decoder& decoder,
+ size_t level) const
{
BER_Object obj = decoder.get_next_object();
@@ -114,48 +49,15 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
that we've gotten the type info */
DER_Encoder encoder;
encoder.add_object(type_tag, class_tag, obj.value);
- std::vector<uint8_t> bits = encoder.get_contents_unlocked();
+ const std::vector<uint8_t> bits = encoder.get_contents_unlocked();
BER_Decoder data(bits);
if(class_tag & CONSTRUCTED)
{
BER_Decoder cons_info(obj.value);
- if(type_tag == SEQUENCE)
- {
- emit(output, "SEQUENCE", level, length);
- decode(output, cons_info, level + 1); // recurse
- }
- else if(type_tag == SET)
- {
- emit(output, "SET", level, length);
- decode(output, cons_info, level + 1); // recurse
- }
- else
- {
- std::string name;
-
- if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
- {
- name = "cons [" + std::to_string(type_tag) + "]";
-
- if(class_tag & APPLICATION)
- {
- name += " appl";
- }
- if(class_tag & CONTEXT_SPECIFIC)
- {
- name += " context";
- }
- }
- else
- {
- name = asn1_tag_to_string(type_tag) + " (cons)";
- }
-
- emit(output, name, level, length);
- decode(output, cons_info, level + 1); // recurse
- }
+ output << format(type_tag, class_tag, level, length, "");
+ decode(output, cons_info, level + 1); // recurse
}
else if((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC))
{
@@ -166,16 +68,21 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
std::vector<uint8_t> inner_bits;
data.decode(inner_bits, type_tag);
BER_Decoder inner(inner_bits);
- decode(output, inner, level + 1); // recurse
+
+ std::ostringstream inner_data;
+ decode(inner_data, inner, level + 1); // recurse
+ output << inner_data.str();
}
catch(...)
{
- emit(output, "[" + std::to_string(type_tag) + "]", level, length, format_binary(bits));
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, bits));
}
}
else
{
- emit(output, "[" + std::to_string(type_tag) + "]", level, length, format_binary(bits));
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, bits));
}
}
else if(type_tag == OBJECT_ID)
@@ -193,7 +100,7 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
out += " [" + oid.as_string() + "]";
}
- emit(output, asn1_tag_to_string(type_tag), level, length, out);
+ output << format(type_tag, class_tag, level, length, out);
}
else if(type_tag == INTEGER || type_tag == ENUMERATED)
{
@@ -216,17 +123,17 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
str += static_cast<char>(rep[i]);
}
- emit(output, asn1_tag_to_string(type_tag), level, length, str);
+ output << format(type_tag, class_tag, level, length, str);
}
else if(type_tag == BOOLEAN)
{
bool boolean;
data.decode(boolean);
- emit(output, asn1_tag_to_string(type_tag), level, length, (boolean ? "true" : "false"));
+ output << format(type_tag, class_tag, level, length, (boolean ? "true" : "false"));
}
else if(type_tag == NULL_TAG)
{
- emit(output, asn1_tag_to_string(type_tag), level, length);
+ output << format(type_tag, class_tag, level, length, "");
}
else if(type_tag == OCTET_STRING || type_tag == BIT_STRING)
{
@@ -238,27 +145,28 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
BER_Decoder inner(decoded_bits);
std::ostringstream inner_data;
- decode(inner_data, inner, level + 1);
+ decode(inner_data, inner, level + 1); // recurse
- emit(output, asn1_tag_to_string(type_tag), level, length, "");
+ output << format(type_tag, class_tag, level, length, "");
output << inner_data.str();
}
catch(...)
{
- emit(output, asn1_tag_to_string(type_tag), level, length, format_binary(decoded_bits));
+ output << format(type_tag, class_tag, level, length,
+ format_bin(type_tag, class_tag, decoded_bits));
}
}
else if(ASN1_String::is_string_type(type_tag))
{
ASN1_String str;
data.decode(str);
- emit(output, asn1_tag_to_string(type_tag), level, length, str.value());
+ output << format(type_tag, class_tag, level, length, str.value());
}
else if(type_tag == UTC_TIME || type_tag == GENERALIZED_TIME)
{
X509_Time time;
data.decode(time);
- emit(output, asn1_tag_to_string(type_tag), level, length, time.readable_string());
+ output << format(type_tag, class_tag, level, length, time.readable_string());
}
else
{
@@ -270,4 +178,102 @@ void ASN1_Pretty_Printer::decode(std::ostream& output,
}
}
+namespace {
+
+std::string format_type(ASN1_Tag type_tag, ASN1_Tag class_tag)
+ {
+ if((class_tag & CONSTRUCTED) && ((class_tag & APPLICATION) || (class_tag & CONTEXT_SPECIFIC)))
+ {
+ std::string name = "cons [" + std::to_string(type_tag) + "]";
+
+ if(class_tag & APPLICATION)
+ {
+ name += " appl";
+ }
+ if(class_tag & CONTEXT_SPECIFIC)
+ {
+ name += " context";
+ }
+
+ return name;
+ }
+ else
+ {
+ return asn1_tag_to_string(type_tag);
+ }
+ }
+
+}
+
+std::string ASN1_Pretty_Printer::format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const
+ {
+ bool should_skip = false;
+
+ if(value.length() > m_print_limit)
+ {
+ should_skip = true;
+ }
+
+ if((type_tag == OCTET_STRING || type_tag == BIT_STRING) &&
+ value.length() > m_print_binary_limit)
+ {
+ should_skip = true;
+ }
+
+ level += m_initial_level;
+
+ std::ostringstream oss;
+
+ oss << " d=" << std::setw(2) << level
+ << ", l=" << std::setw(4) << length << ":"
+ << std::string(level + 1, ' ') << format_type(type_tag, class_tag);
+
+ if(value != "" && !should_skip)
+ {
+ const size_t current_pos = static_cast<size_t>(oss.tellp());
+ const size_t spaces_to_align =
+ (current_pos >= m_value_column) ? 1 : (m_value_column - current_pos);
+
+ oss << std::string(spaces_to_align, ' ') << value;
+ }
+
+ oss << "\n";
+
+ return oss.str();
+ }
+
+std::string ASN1_Pretty_Printer::format_bin(ASN1_Tag /*type_tag*/,
+ ASN1_Tag /*class_tag*/,
+ const std::vector<uint8_t>& vec) const
+ {
+ const size_t unprintable_bound = vec.size() / 4;
+ size_t unprintable = 0;
+
+ std::ostringstream out;
+
+ for(size_t i = 0; i != vec.size(); ++i)
+ {
+ const int c = vec[i];
+ if(std::isalnum(c))
+ {
+ out << static_cast<char>(c);
+ }
+ else
+ {
+ out << "x" << std::hex << static_cast<int>(c) << std::dec;
+ ++unprintable;
+ if(unprintable >= unprintable_bound)
+ {
+ return hex_encode(vec);
+ }
+ }
+ }
+
+ return out.str();
+ }
+
}
diff --git a/src/lib/asn1/asn1_print.h b/src/lib/asn1/asn1_print.h
index 0fd760e89..4f9fa8f4d 100644
--- a/src/lib/asn1/asn1_print.h
+++ b/src/lib/asn1/asn1_print.h
@@ -7,7 +7,7 @@
#ifndef BOTAN_ASN1_PRINT_H_
#define BOTAN_ASN1_PRINT_H_
-#include <botan/types.h>
+#include <botan/asn1_obj.h>
#include <string>
#include <vector>
#include <iosfwd>
@@ -17,27 +17,17 @@ namespace Botan {
class BER_Decoder;
/**
-* Format ASN.1 data into human readable strings
+* Format ASN.1 data and call a virtual to format
*/
-class BOTAN_DLL ASN1_Pretty_Printer
+class BOTAN_DLL ASN1_Formatter
{
public:
+ virtual ~ASN1_Formatter() = default;
+
/**
- * @param print_limit strings larger than this are not printed
- * @param print_binary_limit binary strings larger than this are not printed
* @param print_context_specific if true, try to parse nested context specific data.
- * @param initial_level the initial depth (0 or 1 are the only reasonable values)
- * @param value_column ASN.1 values are lined up at this column in output
*/
- 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 = 60) :
- m_print_limit(print_limit),
- m_print_binary_limit(print_binary_limit),
- m_initial_level(initial_level),
- m_value_column(value_column),
+ ASN1_Formatter(bool print_context_specific) :
m_print_context_specific(print_context_specific)
{}
@@ -53,23 +43,73 @@ class BOTAN_DLL ASN1_Pretty_Printer
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;
+ protected:
+ /**
+ * This is called for each element
+ */
+ virtual std::string format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const = 0;
+
+ /**
+ * This is called to format binary elements that we don't know how to
+ * convert to a string The result will be passed as value to format; the
+ * tags are included as a hint to aid decoding.
+ */
+ virtual std::string format_bin(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const std::vector<uint8_t>& vec) const = 0;
+ private:
void decode(std::ostream& output,
BER_Decoder& decoder,
size_t level) const;
- std::string format_binary(const std::vector<uint8_t>& in) const;
+ const bool m_print_context_specific;
+ };
+
+/**
+* Format ASN.1 data into human readable strings
+*/
+class BOTAN_DLL ASN1_Pretty_Printer final : public ASN1_Formatter
+ {
+ public:
+ /**
+ * @param print_limit strings larger than this are not printed
+ * @param print_binary_limit binary strings larger than this are not printed
+ * @param print_context_specific if true, try to parse nested context specific data.
+ * @param initial_level the initial depth (0 or 1 are the only reasonable values)
+ * @param value_column ASN.1 values are lined up at this column in output
+ */
+ 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 = 60) :
+ ASN1_Formatter(print_context_specific),
+ m_print_limit(print_limit),
+ m_print_binary_limit(print_binary_limit),
+ m_initial_level(initial_level),
+ m_value_column(value_column)
+ {}
+
+ private:
+ std::string format(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ size_t level,
+ size_t length,
+ const std::string& value) const override;
+
+ std::string format_bin(ASN1_Tag type_tag,
+ ASN1_Tag class_tag,
+ const std::vector<uint8_t>& vec) const override;
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;
};
}