/* * (C) 2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include "tests.h" #if defined(BOTAN_HAS_ASN1) #include #include #include #endif namespace Botan_Tests { #if defined(BOTAN_HAS_ASN1) namespace { Test::Result test_ber_stack_recursion() { Test::Result result("BER stack recursion"); // OSS-Fuzz #813 GitHub #989 try { const std::vector in(10000000, 0); Botan::DataSource_Memory input(in.data(), in.size()); Botan::BER_Decoder dec(input); while(dec.more_items()) { Botan::BER_Object obj; dec.get_next(obj); } } catch(Botan::Decoding_Error&) { } result.test_success("No crash"); return result; } Test::Result test_ber_eoc_decoding_limits() { Test::Result result("BER nested indefinite length"); // OSS-Fuzz #4353 Botan::ASN1_Pretty_Printer printer; size_t max_eoc_allowed = 0; for(size_t len = 1; len < 1024; ++len) { std::vector buf(4*len); /* This constructs a len deep sequence of SEQUENCES each with an indefinite length */ for(size_t i = 0; i != 2*len; i += 2) { buf[i ] = 0x30; buf[i+1] = 0x80; } // remainder of values left as zeros (EOC markers) try { printer.print(buf); } catch(Botan::BER_Decoding_Error&) { max_eoc_allowed = len - 1; break; } } result.test_eq("EOC limited to prevent stack exhaustion", max_eoc_allowed, 16); return result; } Test::Result test_asn1_utf8_ascii_parsing() { Test::Result result("ASN.1 ASCII parsing"); try { // \x13 - ASN1 tag for 'printable string' // \x06 - 6 characters of payload // ... - UTF-8 encoded (ASCII chars only) word 'Moscow' const std::string moscow = "\x13\x06\x4D\x6F\x73\x63\x6F\x77"; const std::string moscow_plain = "Moscow"; Botan::DataSource_Memory input(moscow.data()); Botan::BER_Decoder dec(input); Botan::ASN1_String str; str.decode_from(dec); result.test_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_utf8_parsing() { Test::Result result("ASN.1 UTF-8 parsing"); try { // \x0C - ASN1 tag for 'UTF8 string' // \x0C - 12 characters of payload // ... - UTF-8 encoded russian word for Moscow in cyrillic script const std::string moscow = "\x0C\x0C\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; Botan::DataSource_Memory input(moscow.data()); Botan::BER_Decoder dec(input); Botan::ASN1_String str; str.decode_from(dec); result.test_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_ucs2_parsing() { Test::Result result("ASN.1 BMP string (UCS-2) parsing"); try { // \x1E - ASN1 tag for 'BMP (UCS-2) string' // \x0C - 12 characters of payload // ... - UCS-2 encoding for Moscow in cyrillic script const std::string moscow = "\x1E\x0C\x04\x1C\x04\x3E\x04\x41\x04\x3A\x04\x32\x04\x30"; const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; Botan::DataSource_Memory input(moscow.data()); Botan::BER_Decoder dec(input); Botan::ASN1_String str; str.decode_from(dec); result.test_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_ucs4_parsing() { Test::Result result("ASN.1 universal string (UCS-4) parsing"); try { // \x1C - ASN1 tag for 'universal string' // \x18 - 24 characters of payload // ... - UCS-4 encoding for Moscow in cyrillic script const uint8_t moscow[] = "\x1C\x18\x00\x00\x04\x1C\x00\x00\x04\x3E\x00\x00\x04\x41\x00\x00\x04\x3A\x00\x00\x04\x32\x00\x00\x04\x30"; const std::string moscow_plain = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; Botan::DataSource_Memory input(moscow, sizeof(moscow)); Botan::BER_Decoder dec(input); Botan::ASN1_String str; str.decode_from(dec); result.test_eq("value()", str.value(), moscow_plain); } catch(const Botan::Decoding_Error &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_ascii_encoding() { Test::Result result("ASN.1 ASCII encoding"); try { // UTF-8 encoded (ASCII chars only) word 'Moscow' const std::string moscow = "\x4D\x6F\x73\x63\x6F\x77"; Botan::ASN1_String str(moscow); Botan::DER_Encoder enc; str.encode_into(enc); auto encodingResult = enc.get_contents(); // \x13 - ASN1 tag for 'printable string' // \x06 - 6 characters of payload const auto moscowEncoded = Botan::hex_decode("13064D6F73636F77"); result.test_eq("encoding result", encodingResult, moscowEncoded); result.test_success("No crash"); } catch(const std::exception &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_utf8_encoding() { Test::Result result("ASN.1 UTF-8 encoding"); try { // UTF-8 encoded russian word for Moscow in cyrillic script const std::string moscow = "\xD0\x9C\xD0\xBE\xD1\x81\xD0\xBA\xD0\xB2\xD0\xB0"; Botan::ASN1_String str(moscow); Botan::DER_Encoder enc; str.encode_into(enc); auto encodingResult = enc.get_contents(); // \x0C - ASN1 tag for 'UTF8 string' // \x0C - 12 characters of payload const auto moscowEncoded = Botan::hex_decode("0C0CD09CD0BED181D0BAD0B2D0B0"); result.test_eq("encoding result", encodingResult, moscowEncoded); result.test_success("No crash"); } catch(const std::exception &ex) { result.test_failure(ex.what()); } return result; } Test::Result test_asn1_tag_underlying_type() { Test::Result result("ASN.1 class and type underlying type"); if constexpr(std::is_same_v, std::underlying_type_t>) { if constexpr(!std::is_same_v, std::invoke_result_t>) { result.test_failure("Return type of BER_Object::tagging() is different than the underlying type of ASN1_Class"); } else { result.test_success("Same types"); } } else { result.test_failure("ASN1_Class and ASN1_Type have different underlying types"); } return result; } } class ASN1_Tests final : public Test { public: std::vector run() override { std::vector results; results.push_back(test_ber_stack_recursion()); results.push_back(test_ber_eoc_decoding_limits()); results.push_back(test_asn1_utf8_ascii_parsing()); results.push_back(test_asn1_utf8_parsing()); results.push_back(test_asn1_ucs2_parsing()); results.push_back(test_asn1_ucs4_parsing()); results.push_back(test_asn1_ascii_encoding()); results.push_back(test_asn1_utf8_encoding()); results.push_back(test_asn1_tag_underlying_type()); return results; } }; BOTAN_REGISTER_TEST("asn1", "asn1", ASN1_Tests); class ASN1_Time_Parsing_Tests final : public Text_Based_Test { public: ASN1_Time_Parsing_Tests() : Text_Based_Test("asn1_time.vec", "Tspec") {} Test::Result run_one_test(const std::string& tag_str, const VarMap& vars) override { Test::Result result("ASN.1 date parsing"); const std::string tspec = vars.get_req_str("Tspec"); if(tag_str != "UTC" && tag_str != "UTC.invalid" && tag_str != "Generalized" && tag_str != "Generalized.invalid") { throw Test_Error("Invalid tag value in ASN1 date parsing test"); } const Botan::ASN1_Type tag = (tag_str == "UTC" || tag_str == "UTC.invalid") ? Botan::ASN1_Type::UtcTime : Botan::ASN1_Type::GeneralizedTime; const bool valid = tag_str.find(".invalid") == std::string::npos; if(valid) { Botan::ASN1_Time time(tspec, tag); result.test_success("Accepted valid time"); } else { result.test_throws("Invalid time rejected", [=]() { Botan::ASN1_Time time(tspec, tag); }); } return result; } }; BOTAN_REGISTER_TEST("asn1", "asn1_time", ASN1_Time_Parsing_Tests); class ASN1_Printer_Tests final : public Test { public: std::vector run() override { Test::Result result("ASN1_Pretty_Printer"); Botan::ASN1_Pretty_Printer printer; const size_t num_tests = 6; for(size_t i = 1; i <= num_tests; ++i) { std::string i_str = std::to_string(i); const std::vector input1 = Test::read_binary_data_file("asn1_print/input" + i_str + ".der"); const std::string expected1 = Test::read_data_file("asn1_print/output" + i_str + ".txt"); result.test_eq("Test " + i_str, printer.print(input1), expected1); } return {result}; } }; BOTAN_REGISTER_TEST("asn1", "asn1_printer", ASN1_Printer_Tests); #endif }