1 /* 2 * (C) 2016 Jack Lloyd 3 * 4 * Botan is released under the Simplified BSD License (see license.txt) 5 */ 6 7 #include "cli.h" 8 9 #if defined(BOTAN_HAS_TLS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM) 10 11 #include <botan/tls_policy.h> 12 #include <botan/tls_version.h> 13 #include <botan/tls_messages.h> 14 #include <botan/loadstor.h> 15 #include <botan/hex.h> 16 #include <sstream> 17 18 #include "tls_helpers.h" 19 20 namespace Botan_CLI { 21 22 class TLS_Ciphersuites final : public Command 23 { 24 public: TLS_Ciphersuites()25 TLS_Ciphersuites() 26 : Command("tls_ciphers --policy=default --version=tls1.2") {} 27 tls_version_from_str(const std::string & str)28 static Botan::TLS::Protocol_Version::Version_Code tls_version_from_str(const std::string& str) 29 { 30 if(str == "tls1.2" || str == "TLS1.2" || str == "TLS-1.2") 31 { 32 return Botan::TLS::Protocol_Version::TLS_V12; 33 } 34 else if(str == "tls1.1" || str == "TLS1.1" || str == "TLS-1.1") 35 { 36 return Botan::TLS::Protocol_Version::TLS_V11; 37 } 38 else if(str == "tls1.0" || str == "TLS1.1" || str == "TLS-1.1") 39 { 40 return Botan::TLS::Protocol_Version::TLS_V10; 41 } 42 if(str == "dtls1.2" || str == "DTLS1.2" || str == "DTLS-1.2") 43 { 44 return Botan::TLS::Protocol_Version::DTLS_V12; 45 } 46 else if(str == "dtls1.0" || str == "DTLS1.0" || str == "DTLS-1.0") 47 { 48 return Botan::TLS::Protocol_Version::DTLS_V10; 49 } 50 else 51 { 52 throw CLI_Error("Unknown TLS version '" + str + "'"); 53 } 54 } 55 group() const56 std::string group() const override 57 { 58 return "tls"; 59 } 60 description() const61 std::string description() const override 62 { 63 return "Lists all ciphersuites for a policy and TLS version"; 64 } 65 go()66 void go() override 67 { 68 const std::string policy_type = get_arg("policy"); 69 const Botan::TLS::Protocol_Version version(tls_version_from_str(get_arg("version"))); 70 const bool with_srp = false; // fixme 71 72 auto policy = load_tls_policy(policy_type); 73 74 if(policy->acceptable_protocol_version(version) == false) 75 { 76 error_output() << "Error: the policy specified does not allow the given TLS version\n"; 77 return; 78 } 79 80 for(uint16_t suite_id : policy->ciphersuite_list(version, with_srp)) 81 { 82 const Botan::TLS::Ciphersuite suite(Botan::TLS::Ciphersuite::by_id(suite_id)); 83 output() << suite.to_string() << "\n"; 84 } 85 } 86 }; 87 88 BOTAN_REGISTER_COMMAND("tls_ciphers", TLS_Ciphersuites); 89 90 class TLS_Client_Hello_Reader final : public Command 91 { 92 public: TLS_Client_Hello_Reader()93 TLS_Client_Hello_Reader() 94 : Command("tls_client_hello --hex input") {} 95 group() const96 std::string group() const override 97 { 98 return "tls"; 99 } 100 description() const101 std::string description() const override 102 { 103 return "Parse a TLS client hello message"; 104 } 105 go()106 void go() override 107 { 108 const std::string input_file = get_arg("input"); 109 std::vector<uint8_t> input; 110 111 if(flag_set("hex")) 112 { 113 input = Botan::hex_decode(slurp_file_as_str(input_file)); 114 } 115 else 116 { 117 input = slurp_file(input_file); 118 } 119 120 if(input.size() < 45) 121 { 122 error_output() << "Input too short to be valid\n"; 123 return; 124 } 125 126 // Input also contains the record layer header, strip it 127 if(input[0] == 22) 128 { 129 const size_t len = Botan::make_uint16(input[3], input[4]); 130 131 if(input.size() != len + 5) 132 { 133 error_output() << "Record layer length invalid\n"; 134 return; 135 } 136 137 input = std::vector<uint8_t>(input.begin() + 5, input.end()); 138 } 139 140 // Assume the handshake header is there, strip it 141 if(input[0] == 1) 142 { 143 const size_t hs_len = Botan::make_uint32(0, input[1], input[2], input[3]); 144 145 if(input.size() != hs_len + 4) 146 { 147 error_output() << "Handshake layer length invalid\n"; 148 return; 149 } 150 151 input = std::vector<uint8_t>(input.begin() + 4, input.end()); 152 } 153 154 try 155 { 156 Botan::TLS::Client_Hello hello(input); 157 158 output() << format_hello(hello); 159 } 160 catch(std::exception& e) 161 { 162 error_output() << "Parsing client hello failed: " << e.what() << "\n"; 163 } 164 } 165 166 private: format_hello(const Botan::TLS::Client_Hello & hello)167 std::string format_hello(const Botan::TLS::Client_Hello& hello) 168 { 169 std::ostringstream oss; 170 oss << "Version: " << hello.version().to_string() << "\n" 171 << "Random: " << Botan::hex_encode(hello.random()) << "\n"; 172 173 if(!hello.session_id().empty()) 174 oss << "SessionID: " << Botan::hex_encode(hello.session_id()) << "\n"; 175 for(uint16_t csuite_id : hello.ciphersuites()) 176 { 177 auto csuite = Botan::TLS::Ciphersuite::by_id(csuite_id); 178 if(csuite.valid()) 179 oss << "Cipher: " << csuite.to_string() << "\n"; 180 else if(csuite_id == 0x00FF) 181 oss << "Cipher: EMPTY_RENEGOTIATION_INFO_SCSV\n"; 182 else 183 oss << "Cipher: Unknown (" << std::hex << csuite_id << ")\n"; 184 } 185 186 oss << "Supported signature schemes: "; 187 188 if(hello.signature_schemes().empty()) 189 { 190 oss << "Did not send signature_algorithms extension\n"; 191 } 192 else 193 { 194 for(Botan::TLS::Signature_Scheme scheme : hello.signature_schemes()) 195 { 196 try 197 { 198 auto s = sig_scheme_to_string(scheme); 199 oss << s << " "; 200 } 201 catch(...) 202 { 203 oss << "(" << std::hex << static_cast<uint16_t>(scheme) << ") "; 204 } 205 } 206 oss << "\n"; 207 } 208 209 std::map<std::string, bool> hello_flags; 210 hello_flags["ALPN"] = hello.supports_alpn(); 211 hello_flags["Encrypt Then Mac"] = hello.supports_encrypt_then_mac(); 212 hello_flags["Extended Master Secret"] = hello.supports_extended_master_secret(); 213 hello_flags["Session Ticket"] = hello.supports_session_ticket(); 214 215 for(auto&& i : hello_flags) 216 oss << "Supports " << i.first << "? " << (i.second ? "yes" : "no") << "\n"; 217 218 return oss.str(); 219 } 220 }; 221 222 BOTAN_REGISTER_COMMAND("tls_client_hello", TLS_Client_Hello_Reader); 223 224 } 225 226 #endif 227