• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 21863608093

10 Feb 2026 11:46AM UTC coverage: 90.064% (-0.004%) from 90.068%
21863608093

Pull #5303

github

web-flow
Merge aea1c629d into 1d119e57a
Pull Request #5303: Refactor: Organize TLS Extensions into TLS 1.2 and 1.3 Modules

102231 of 113509 relevant lines covered (90.06%)

11483899.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.59
/src/cli/tls_utils.cpp
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/hex.h>
12
   #include <botan/tls_ciphersuite.h>
13
   #include <botan/tls_messages.h>
14
   #include <botan/tls_version.h>
15
   #include <botan/internal/fmt.h>
16
   #include <botan/internal/loadstor.h>
17
   #include <botan/internal/stl_util.h>
18
   #include <sstream>
19

20
   #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13)
21
      #include <botan/tls_extensions_13.h>
22
      #include <botan/tls_messages_12.h>
23
      #include <botan/tls_messages_13.h>
24
   #endif
25

26
   #include "tls_helpers.h"
27

28
namespace Botan_CLI {
29

30
class TLS_Ciphersuites final : public Command {
31
   public:
32
      TLS_Ciphersuites() : Command("tls_ciphers --policy=default --version=tls1.2") {}
12✔
33

34
      static Botan::TLS::Protocol_Version tls_version_from_str(const std::string& str) {
5✔
35
         if(str == "tls1.2" || str == "TLS1.2" || str == "TLS-1.2") {
5✔
36
            return Botan::TLS::Protocol_Version::TLS_V12;
5✔
37
         }
38
         if(str == "dtls1.2" || str == "DTLS1.2" || str == "DTLS-1.2") {
×
39
            return Botan::TLS::Protocol_Version::DTLS_V12;
×
40
         } else {
41
            throw CLI_Error("Unknown TLS version '" + str + "'");
×
42
         }
43
      }
44

45
      std::string group() const override { return "tls"; }
1✔
46

47
      std::string description() const override { return "Lists all ciphersuites for a policy and TLS version"; }
1✔
48

49
      void go() override {
5✔
50
         const std::string policy_type = get_arg("policy");
5✔
51
         const Botan::TLS::Protocol_Version version = tls_version_from_str(get_arg("version"));
10✔
52

53
         auto policy = load_tls_policy(policy_type);
5✔
54

55
         if(!policy->acceptable_protocol_version(version)) {
5✔
56
            error_output() << "Error: the policy specified does not allow the given TLS version\n";
×
57
            return;
×
58
         }
59

60
         for(const uint16_t suite_id : policy->ciphersuite_list(version)) {
119✔
61
            const auto s = Botan::TLS::Ciphersuite::by_id(suite_id);
114✔
62
            output() << ((s) ? s->to_string() : "unknown cipher suite") << "\n";
456✔
63
         }
5✔
64
      }
5✔
65
};
66

67
BOTAN_REGISTER_COMMAND("tls_ciphers", TLS_Ciphersuites);
6✔
68

69
   #if defined(BOTAN_HAS_TLS_12) && defined(BOTAN_HAS_TLS_13)
70

71
class TLS_Client_Hello_Reader final : public Command {
72
   public:
73
      TLS_Client_Hello_Reader() : Command("tls_client_hello --hex input") {}
6✔
74

75
      std::string group() const override { return "tls"; }
1✔
76

77
      std::string description() const override { return "Parse a TLS client hello message"; }
1✔
78

79
      void go() override {
2✔
80
         const std::string input_file = get_arg("input");
2✔
81
         std::vector<uint8_t> input;
2✔
82

83
         if(flag_set("hex")) {
2✔
84
            input = Botan::hex_decode(slurp_file_as_str(input_file));
4✔
85
         } else {
86
            input = slurp_file(input_file);
×
87
         }
88

89
         if(input.size() < 45) {
2✔
90
            error_output() << "Input too short to be valid\n";
×
91
            return;
92
         }
93

94
         // Input also contains the record layer header, strip it
95
         if(input[0] == 22) {
2✔
96
            const size_t len = Botan::make_uint16(input[3], input[4]);
2✔
97

98
            if(input.size() != len + 5) {
2✔
99
               error_output() << "Record layer length invalid\n";
×
100
               return;
101
            }
102

103
            input = std::vector<uint8_t>(input.begin() + 5, input.end());
4✔
104
         }
105

106
         // Assume the handshake header is there, strip it
107
         if(input[0] == 1) {
2✔
108
            const size_t hs_len = Botan::make_uint32(0, input[1], input[2], input[3]);
2✔
109

110
            if(input.size() != hs_len + 4) {
2✔
111
               error_output() << "Handshake layer length invalid\n";
×
112
               return;
113
            }
114

115
            input = std::vector<uint8_t>(input.begin() + 4, input.end());
4✔
116
         }
117

118
         try {
2✔
119
            output() << format_hello([&]() -> std::variant<Botan::TLS::Client_Hello_13, Botan::TLS::Client_Hello_12> {
6✔
120
               auto data = Botan::TLS::Client_Hello_13::parse(input);
2✔
121
               if(std::holds_alternative<Botan::TLS::Client_Hello_13>(data)) {
2✔
122
                  return std::get<Botan::TLS::Client_Hello_13>(std::move(data));
1✔
123
               } else {
124
                  return Botan::TLS::Client_Hello_12(input);
2✔
125
               }
126
            }());
8✔
127
         } catch(std::exception& e) {
×
128
            error_output() << "Parsing client hello failed: " << e.what() << "\n";
×
129
         }
×
130
      }
2✔
131

132
   private:
133
      static std::string format_hello(
2✔
134
         const std::variant<Botan::TLS::Client_Hello_13, Botan::TLS::Client_Hello_12>& hello) {
135
         std::ostringstream oss;
2✔
136

137
         const auto* hello_base =
2✔
138
            std::visit([](const auto& ch) -> const Botan::TLS::Client_Hello* { return &ch; }, hello);
2✔
139

140
         const std::string version = std::visit(Botan::overloaded{
4✔
141
                                                   [](const Botan::TLS::Client_Hello_12&) { return "1.2"; },
1✔
142
                                                   [](const Botan::TLS::Client_Hello_13&) { return "1.3"; },
143
                                                },
144
                                                hello);
3✔
145

146
         oss << "Version: " << version << "\n"
2✔
147
             << "Random: " << Botan::hex_encode(hello_base->random()) << "\n";
2✔
148

149
         if(!hello_base->session_id().empty()) {
2✔
150
            oss << "SessionID: " << Botan::hex_encode(hello_base->session_id().get()) << "\n";
2✔
151
         }
152
         for(const uint16_t csuite_id : hello_base->ciphersuites()) {
43✔
153
            const auto csuite = Botan::TLS::Ciphersuite::by_id(csuite_id);
41✔
154
            if(csuite && csuite->valid()) {
41✔
155
               oss << "Cipher: " << csuite->to_string() << "\n";
111✔
156
            } else if(csuite_id == 0x00FF) {
4✔
157
               oss << "Cipher: EMPTY_RENEGOTIATION_INFO_SCSV\n";
×
158
            } else {
159
               oss << "Cipher: Unknown (" << std::hex << csuite_id << ")\n";
4✔
160
            }
161
         }
162

163
         oss << "Supported signature schemes: ";
2✔
164

165
         if(hello_base->signature_schemes().empty()) {
2✔
166
            oss << "Did not send signature_algorithms extension\n";
×
167
         } else {
168
            for(const Botan::TLS::Signature_Scheme scheme : hello_base->signature_schemes()) {
20✔
169
               try {
18✔
170
                  auto s = scheme.to_string();
18✔
171
                  oss << s << " ";
18✔
172
               } catch(...) {
18✔
173
                  oss << "(" << std::hex << static_cast<unsigned int>(scheme.wire_code()) << ") ";
×
174
               }
×
175
            }
2✔
176
            oss << "\n";
2✔
177
         }
178

179
         if(auto* sg = hello_base->extensions().get<Botan::TLS::Supported_Groups>()) {
2✔
180
            oss << "Supported Groups: ";
2✔
181
            for(const auto group : sg->groups()) {
26✔
182
               oss << group.to_string().value_or(Botan::fmt("Unknown group: {}", group.wire_code())) << " ";
96✔
183
            }
184
            oss << "\n";
2✔
185
         }
186

187
         std::map<std::string, bool> hello_flags;
2✔
188
         hello_flags["ALPN"] = hello_base->supports_alpn();
2✔
189

190
         std::visit(Botan::overloaded{
2✔
191
                       [&](const Botan::TLS::Client_Hello_12& ch12) {
1✔
192
                          hello_flags["Encrypt Then Mac"] = ch12.supports_encrypt_then_mac();
1✔
193
                          hello_flags["Extended Master Secret"] = ch12.supports_extended_master_secret();
1✔
194
                          hello_flags["Session Ticket"] = ch12.supports_session_ticket();
1✔
195
                       },
1✔
196
                       [&](const Botan::TLS::Client_Hello_13& ch13) {
1✔
197
                          if(auto* ks = ch13.extensions().get<Botan::TLS::Key_Share>()) {
1✔
198
                             oss << "Key Shares: ";
1✔
199
                             for(const auto group : ks->offered_groups()) {
2✔
200
                                oss << group.to_string().value_or(Botan::fmt("Unknown group: {}", group.wire_code()))
4✔
201
                                    << " ";
2✔
202
                             }
1✔
203
                             oss << "\n";
1✔
204
                          }
205
                       },
1✔
206
                    },
207
                    hello);
208

209
         for(auto&& i : hello_flags) {
7✔
210
            oss << "Supports " << i.first << "? " << (i.second ? "yes" : "no") << "\n";
7✔
211
         }
212

213
         return oss.str();
4✔
214
      }
2✔
215
};
216

217
BOTAN_REGISTER_COMMAND("tls_client_hello", TLS_Client_Hello_Reader);
3✔
218

219
   #endif
220

221
}  // namespace Botan_CLI
222

223
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc