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

randombit / botan / 20283898778

16 Dec 2025 09:52PM UTC coverage: 90.52% (+0.2%) from 90.36%
20283898778

Pull #5167

github

web-flow
Merge 795a38954 into 3d96b675e
Pull Request #5167: Changes to reduce unnecessary inclusions

101154 of 111748 relevant lines covered (90.52%)

12682929.61 hits per line

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

80.47
/src/cli/tls_helpers.h
1
/*
2
* (C) 2014,2015,2019 Jack Lloyd
3
*     2023           René Meusel, Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#ifndef BOTAN_CLI_TLS_HELPERS_H_
9
#define BOTAN_CLI_TLS_HELPERS_H_
10

11
#include <botan/assert.h>
12
#include <botan/credentials_manager.h>
13
#include <botan/data_src.h>
14
#include <botan/hex.h>
15
#include <botan/pkcs8.h>
16
#include <botan/tls_policy.h>
17
#include <botan/x509_key.h>
18
#include <botan/x509self.h>
19
#include <fstream>
20
#include <memory>
21
#include <utility>
22

23
#include "cli_exceptions.h"
24

25
#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
26
   #include <botan/certstor_system.h>
27
#endif
28

29
inline bool value_exists(const std::vector<std::string>& vec, const std::string& val) {
×
30
   for(const auto& v : vec) {
×
31
      if(v == val) {
×
32
         return true;
33
      }
34
   }
35
   return false;
36
}
37

38
inline std::string maybe_hex_encode(std::string_view v) {
4✔
39
   auto is_printable_char = [](uint8_t c) { return c >= 32 && c < 127; };
36✔
40

41
   for(char c : v) {
36✔
42
      if(!is_printable_char(c)) {
32✔
43
         return Botan::hex_encode(std::span<const uint8_t>(reinterpret_cast<const uint8_t*>(v.data()), v.size()));
×
44
      }
45
   }
46

47
   return std::string(v);
4✔
48
}
49

50
class Basic_Credentials_Manager : public Botan::Credentials_Manager {
51
   protected:
52
      void load_credentials(const std::string& cred, const std::string& key) {
47✔
53
         Botan::DataSource_Stream key_in(key);
47✔
54
         auto privkey = Botan::PKCS8::load_key(key_in);
47✔
55

56
         // first try to read @p cred as a public key
57
         try {
47✔
58
            auto pubkey = Botan::X509::load_key(cred);
47✔
59
            m_raw_pubkey = {std::exchange(privkey, {}), std::move(pubkey)};
×
60
            return;
×
61
         } catch(const Botan::Decoding_Error&) {}
47✔
62

63
         // ... then try again assuming that @p cred contains a certificate chain
64
         BOTAN_ASSERT_NONNULL(privkey);
47✔
65
         Certificate_Info cert;
47✔
66
         cert.key = std::move(privkey);
47✔
67

68
         Botan::DataSource_Stream in(cred);
47✔
69
         while(!in.end_of_data()) {
141✔
70
            try {
94✔
71
               cert.certs.push_back(Botan::X509_Certificate(in));
141✔
72
            } catch(std::exception&) {}
47✔
73
         }
74

75
         BOTAN_ARG_CHECK(!cert.certs.empty(), "Provided certificate chain file cannot be loaded");
47✔
76

77
         // TODO: attempt to validate chain ourselves
78

79
         m_certs.push_back(cert);
47✔
80
      }
47✔
81

82
   public:
83
      Basic_Credentials_Manager(bool use_system_store,
54✔
84
                                const std::string& ca_path,
85
                                std::optional<std::string> client_cred = std::nullopt,
86
                                std::optional<std::string> client_key = std::nullopt,
87
                                std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
88
                                std::optional<std::string> psk_identity = std::nullopt,
89
                                const std::optional<std::string>& psk_prf = std::nullopt) :
54✔
90
            m_psk(std::move(psk)),
54✔
91
            m_psk_identity(std::move(psk_identity)),
54✔
92

93
            // RFC 8446 4.2.11
94
            //    the Hash algorithm MUST be set when the PSK is established or
95
            //    default to SHA-256 if no such algorithm is defined.
96
            m_psk_prf(psk_prf.value_or("SHA-256")) {
108✔
97
         if(!ca_path.empty()) {
54✔
98
            m_certstores.push_back(std::make_shared<Botan::Certificate_Store_In_Memory>(ca_path));
10✔
99
         }
100

101
         BOTAN_ARG_CHECK(client_cred.has_value() == client_key.has_value(),
54✔
102
                         "either provide both client certificate and key or neither");
103

104
         if(client_cred.has_value() && client_key.has_value()) {
54✔
105
            load_credentials(client_cred.value(), client_key.value());
×
106
         }
107

108
#if defined(BOTAN_HAS_CERTSTOR_SYSTEM)
109
         if(use_system_store) {
54✔
110
            m_certstores.push_back(std::make_shared<Botan::System_Certificate_Store>());
54✔
111
         }
112
#else
113
         BOTAN_UNUSED(use_system_store);
114
#endif
115
      }
54✔
116

117
      Basic_Credentials_Manager(const std::string& server_cred,
47✔
118
                                const std::string& server_key,
119
                                std::optional<Botan::secure_vector<uint8_t>> psk = std::nullopt,
120
                                std::optional<std::string> psk_identity = std::nullopt,
121
                                const std::optional<std::string>& psk_prf = std::nullopt) :
47✔
122
            m_psk(std::move(psk)),
47✔
123
            m_psk_identity(std::move(psk_identity)),
47✔
124

125
            // RFC 8446 4.2.11
126
            //    the Hash algorithm MUST be set when the PSK is established or
127
            //    default to SHA-256 if no such algorithm is defined.
128
            m_psk_prf(psk_prf.value_or("SHA-256")) {
94✔
129
         load_credentials(server_cred, server_key);
47✔
130
      }
47✔
131

132
      std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
96✔
133
                                                                             const std::string& /*hostname*/) override {
134
         std::vector<Botan::Certificate_Store*> v;
96✔
135

136
         // don't ask for client certs
137
         if(type == "tls-server") {
96✔
138
            return v;
139
         }
140

141
         for(const auto& cs : m_certstores) {
93✔
142
            v.push_back(cs.get());
50✔
143
         }
144

145
         return v;
146
      }
×
147

148
      std::vector<Botan::X509_Certificate> find_cert_chain(
86✔
149
         const std::vector<std::string>& algos,
150
         const std::vector<Botan::AlgorithmIdentifier>& cert_signature_schemes,
151
         const std::vector<Botan::X509_DN>& acceptable_cas,
152
         const std::string& type,
153
         const std::string& hostname) override {
154
         BOTAN_UNUSED(cert_signature_schemes);
86✔
155

156
         if(type == "tls-client") {
86✔
157
            for(const auto& dn : acceptable_cas) {
×
158
               for(const auto& cred : m_certs) {
×
159
                  if(dn == cred.certs[0].issuer_dn()) {
×
160
                     return cred.certs;
×
161
                  }
162
               }
163
            }
164
         } else if(type == "tls-server") {
86✔
165
            for(const auto& i : m_certs) {
120✔
166
               if(std::find(algos.begin(), algos.end(), i.key->algo_name()) == algos.end()) {
86✔
167
                  continue;
34✔
168
               }
169

170
               if(!hostname.empty() && !i.certs[0].matches_dns_name(hostname)) {
52✔
171
                  continue;
×
172
               }
173

174
               return i.certs;
52✔
175
            }
176
         }
177

178
         return {};
34✔
179
      }
180

181
      std::shared_ptr<Botan::Public_Key> find_raw_public_key(const std::vector<std::string>& algos,
×
182
                                                             const std::string& type,
183
                                                             const std::string& hostname) override {
184
         BOTAN_UNUSED(type, hostname);
×
185
         return (algos.empty() || value_exists(algos, m_raw_pubkey.public_key->algo_name())) ? m_raw_pubkey.public_key
×
186
                                                                                             : nullptr;
×
187
      }
188

189
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::X509_Certificate& cert,
51✔
190
                                                          const std::string& /*type*/,
191
                                                          const std::string& /*context*/) override {
192
         for(const auto& i : m_certs) {
51✔
193
            if(cert == i.certs[0]) {
51✔
194
               return i.key;
51✔
195
            }
196
         }
197

198
         return nullptr;
×
199
      }
200

201
      std::shared_ptr<Botan::Private_Key> private_key_for(const Botan::Public_Key& raw_public_key,
×
202
                                                          const std::string& /*type*/,
203
                                                          const std::string& /*context*/) override {
204
         return (m_raw_pubkey.public_key->fingerprint_public() == raw_public_key.fingerprint_public())
×
205
                   ? m_raw_pubkey.private_key
×
206
                   : nullptr;
×
207
      }
208

209
      std::string psk_identity(const std::string& type,
1✔
210
                               const std::string& context,
211
                               const std::string& identity_hint) override {
212
         return (m_psk_identity && (type == "tls-client" || type == "tls-server"))
1✔
213
                   ? m_psk_identity.value()
2✔
214
                   : Botan::Credentials_Manager::psk_identity(type, context, identity_hint);
1✔
215
      }
216

217
      std::vector<Botan::TLS::ExternalPSK> find_preshared_keys(
32✔
218
         std::string_view host,
219
         Botan::TLS::Connection_Side peer_type,
220
         const std::vector<std::string>& ids = {},
221
         const std::optional<std::string>& prf = std::nullopt) override {
222
         if(!m_psk.has_value() || !m_psk_identity.has_value()) {
32✔
223
            return Botan::Credentials_Manager::find_preshared_keys(host, peer_type, ids, prf);
28✔
224
         }
225

226
         std::vector<Botan::TLS::ExternalPSK> psks;
4✔
227

228
         const bool prf_matches = !prf.has_value() || m_psk_prf == prf.value();
4✔
229
         const bool id_matches = ids.empty() || std::find(ids.begin(), ids.end(), m_psk_identity.value()) != ids.end();
4✔
230

231
         if(prf_matches && id_matches) {
4✔
232
            psks.emplace_back(m_psk_identity.value(), m_psk_prf, m_psk.value());
4✔
233
         }
234

235
         return psks;
4✔
236
      }
4✔
237

238
   private:
239
      struct Certificate_Info {
47✔
240
            std::vector<Botan::X509_Certificate> certs;
241
            std::shared_ptr<Botan::Private_Key> key;
242
      };
243

244
      struct RawPublicKey_Info {
101✔
245
            std::shared_ptr<Botan::Private_Key> private_key;
246
            std::shared_ptr<Botan::Public_Key> public_key;
247
      };
248

249
      std::vector<Certificate_Info> m_certs;
250
      RawPublicKey_Info m_raw_pubkey;
251

252
      std::vector<std::shared_ptr<Botan::Certificate_Store>> m_certstores;
253
      std::optional<Botan::secure_vector<uint8_t>> m_psk;
254
      std::optional<std::string> m_psk_identity;
255
      std::string m_psk_prf;
256
};
257

258
class TLS_All_Policy final : public Botan::TLS::Policy {
2✔
259
   public:
260
      std::vector<std::string> allowed_ciphers() const override {
98✔
261
         return std::vector<std::string>{"AES-256/GCM",
98✔
262
                                         "AES-128/GCM",
263
                                         "ChaCha20Poly1305",
264
                                         "AES-256/OCB(12)",
265
                                         "AES-256/CCM",
266
                                         "AES-128/CCM",
267
                                         "AES-256/CCM(8)",
268
                                         "AES-128/CCM(8)",
269
                                         "Camellia-256/GCM",
270
                                         "Camellia-128/GCM",
271
                                         "ARIA-256/GCM",
272
                                         "ARIA-128/GCM",
273
                                         "AES-256",
274
                                         "AES-128",
275
                                         "Camellia-256",
276
                                         "Camellia-128",
277
                                         "SEED",
278
                                         "3DES",
279
                                         "NULL"};
2,058✔
280
      }
196✔
281

282
      std::vector<std::string> allowed_key_exchange_methods() const override {
1✔
283
         return {"ECDHE_PSK", "DHE_PSK", "PSK", "ECDH", "DH", "RSA"};
1✔
284
      }
285

286
      std::vector<std::string> allowed_signature_methods() const override {
1✔
287
         return {"ECDSA", "RSA", "DSA", "IMPLICIT"};
1✔
288
      }
289

290
      bool allow_tls12() const override { return true; }
1✔
291
};
292

293
inline std::shared_ptr<Botan::TLS::Policy> load_tls_policy(const std::string& policy_type) {
18✔
294
   if(policy_type == "default" || policy_type.empty()) {
18✔
295
      return std::make_shared<Botan::TLS::Policy>();
3✔
296
   } else if(policy_type == "suiteb_128") {
15✔
297
      return std::make_shared<Botan::TLS::NSA_Suite_B_128>();
1✔
298
   } else if(policy_type == "suiteb_192" || policy_type == "suiteb") {
14✔
299
      return std::make_shared<Botan::TLS::NSA_Suite_B_192>();
1✔
300
   } else if(policy_type == "strict") {
13✔
301
      return std::make_shared<Botan::TLS::Strict_Policy>();
1✔
302
   } else if(policy_type == "bsi") {
12✔
303
      return std::make_shared<Botan::TLS::BSI_TR_02102_2>();
×
304
   } else if(policy_type == "datagram") {
12✔
305
      return std::make_shared<Botan::TLS::Datagram_Policy>();
×
306
   } else if(policy_type == "all" || policy_type == "everything") {
12✔
307
      return std::make_shared<TLS_All_Policy>();
1✔
308
   }
309

310
   // if something we don't recognize, assume it's a file
311
   std::ifstream policy_stream(policy_type);
11✔
312
   if(!policy_stream.good()) {
11✔
313
      throw Botan_CLI::CLI_Usage_Error("Unknown TLS policy: not a file or known short name");
×
314
   }
315
   return std::make_shared<Botan::TLS::Text_Policy>(policy_stream);
11✔
316
}
11✔
317

318
#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